热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

一个复合查询方法_MySQL

几乎每个完整的应用程序都会需要一个复合查询。建立一个功能强大的复合查询首先必须要能够动态生成查询条件,其次应该能够对查询到的数据进行修改,最后这个复合查询最好能够对一对多的两个表建立条件进行查询。在VFP里建立查询的方法主要有这么几种:一是使用VFP中自
几乎每个完整的应用程序都会需要一个复合查询。建立一个功能强大的复合查询首先必须要能够动态生成查询条件,其次应该能够对查询到的数据进行修改,最后这个复合查询最好能够对一对多的两个表建立条件进行查询。

在VFP里建立查询的方法主要有这么几种:一是使用VFP中自带的SearchClass类;二是建立一个查询;三是建立一个视图,其中包括参数化视图、宏替换Sql语句视图;四是建立一个Grid,将其数据源设置为SQL语句或临时表。

不管哪一种方法,其实质都是使用SQL语句。

这几种方法各有各的优点,也都有缺点。

建立查询的方法最死板,只能建立固定条件的查询,并且不能更新数据,最不能满足要求。

SearchClass类功能强大,但是它只能对一个表建立条件进行查询,并且它的源代码太复杂了,几乎难以进行修改定制;(初学者想必都有过用表单向导建立表单后试图修改txtbtn类、SearchClass类的经历吧!看到源代码后有几个没昏倒?)用将Grid的数据源设置为SQL语句或临时表的方法无法修改/更新数据,刷新数据也比较困难。(这方面的问题在网易虚拟社区VFP版上有过许多讨论,大家可以去看看。)建立视图的方法中,参数化视图也太简单。不管是用表单控件的值作参数还是用给参数两端加上引号的方法都只能对固定的字段进行查询。如果是复合查询,难道要先建立几十个视图吗?最有前途的办法还是用宏替换SQL语句建立视图的办法。视图有着能够对数据进行修改/更新的优点,如果能够动态生成查询条件,那么就是最完美的查询了。

建立宏替换sql语句视图的具体办法是先动态生成一个Sql语句sqlstatement,然后用宏替换的方法使用Create Sql view viewname as &sqlstatement来动态建立视图,最后将数据动态显示在一个Grid控件中。

看到这里,VFP大虾们怕会大喊:Stop!你当我是菜鸟啊!你的办法从理论上虽然行的通,但实际做起来就会碰到查询结果在表格上数据无法刷新的难题。俺早就试过不行了!你想骗稿费啊!

嘿嘿,这个难题偏偏给我解决了!这就是我洋洋得意的写这篇文章的原因!

问题的解决

==========

几个月前(好可怜^0^.....),我参照VFP的Sample中的Solution里的Interactively Bulid a sqlstatement示例建立了一个复合查询,想将它集成到我自己的程序中。由于该示例是用browse窗口来显示查询结果的,而我自己的应用程序使用了顶层表单,结果编译后运行才发现Browse窗口在顶层表单中无法显示。于是我建立了一个有两个表单的表单集,用一个表单makesql动态生成sql语句,在另一个表单form1上用grid来显示查询结果的数据,在给Grid设置数据源的时候,问题来了。首先使用临时表来作为表格的数据源,结果第一次查询正确,更改条件进行第二次查询时碰到了众所周知的"不能更新临时表"的错误;使用sql语句倒是可以,可是在表格显示数据前会莫名其妙的先出现一个Browse窗口,必须关闭它后才会显示表格,由于browse窗口在使用顶层表单的程序中无法显示,结果导致程序无法继续执行,这个办法也不行。

最后只能使用Create sql view viewname as &sqlstatement的办法了,先随便建立一个视图tempview,把表格的Recordsourcetype属性设置为1-别名,Recordsource属性设置为视图别名tempview,在表单makesql上建立sql语句后的代码中使用create sql view temp view as &sqlstatement建立视图Tempview.
执行后发现,第一次查询正确,更改查询条件后再次查询,出现"视图已存在,要改写吗?"的情况,按下"确定"后,出现的表格中没有数据。

避免出现对话框的问题好解决,在建立视图前先用rename view tempview to oldview,然后用Delete view oldview将旧的视图删除就可以了。代码如下:

****************************************************************************

set database to databasename &&databasename是你的数据库名称

&&注意:即使你打开了数据库也必须写这个语句!否则会出现"找不到数据库"的错误。

if used("tempview")

rename view tempview to oldview

delete view oldview

endif

Create sql view tempview as &sqlstatement

=requery()

IF _TALLY = 0

#DEFINE MSG_LOC "没有找到符合条件的纪录!"

#DEFINE TITLE_LOC "没有找到纪录"

=MESSAGEBOX(MSG_LOC,64 0 0,TITLE_LOC)

ELSE

thisform.hide

thisformset.form1.show

Endif

*****************************************************************************

但是这样做了以后,表格上没有数据的问题仍然存在。查找资料后发现,用Create sql view语句编程建立视图的方法,建立视图后要先保存视图定义,再打开视图后视图中才有数据。因此,必须将Creatsql view语句部分代码修改如下:

************************************************

Create sql view tempview as &sqlstatement

use

use tempview

************************************************

满以为这下问题解决了,结果更惨。出现的表格上不但没有数据,连表头、网格都不见了!这个问题

百思不得其解,查找资料也没有结果,最后不了了之,一直困扰了我几个月。

就在昨晚,我上床的时候突然灵光一现:既然表格无法动态加载数据源视图,那么干脆连包含表格的表单也动态生成!只要表单动态生成,那么表单上的表格对象的数据源不就完全重新加载了吗?也用不着刷新什么的了!而且,用这个方法也用不着先建立一个tempview视图,完全在程序中动态生成就可以了。主意一定,马上下床动手。将原来包含表格的表单form1删除,在上述的代码中最后一句

thisformset.form1.show前插入以下代码:

*************************************************************

thisformset.addobject("form1","form")

with thisformset.form1

.caption="查询结果"

.width=600

.height=400

.Autocenter=.t.

.cOntrolbox=.f.

endwith

thisformset.form1.addobject("cmdReturn1","cmdReturn")

with thisformset.form1.cmdReturn1

.top=360

.left=270

endwith

thisformset.form1.addobject("grid1","gird")

with thisformset.form1.grid1

.Recordsourcetype=1

.Recordsource="tempview"

.top=10

.left=20

.height=300

.width=560

endwith

**************************************************************

在程序的最后加入:

*********************************************

Define class cmdReturn as commandbutton

caption="返回"

procdure click

thisform.release

endproc

enddefine

*********************************************

这下总可以了吧?运行程序,结果出现对话框"在事件或方法中不能嵌套类定义!"。我@#$%&*....什

么嘛!教科书、帮助文件中的示例prg都是这么写的啊!

不过好在还有办法,我手工建立一个类总行了吧!

在类库mybut中新建一个按钮类cmdReturn,设置它的cation属性为"返回",click事件代码为thisform.release。在上面的代码前插入set Classlib to mybut additive(注意:如果不加additive参数,将关闭所有之前打开的类库!),然后将最后的类定义语句Define...EndDefine全部删除。

运行,新表单出现!且慢,这个表单上怎么什么东西都没有?:-((打开调试器,在"局部"窗口中察看,发现明明有cmdReturn1、Grid1对象啊!怎么回事?仔细察看他们的每个属性,发现原来它们的visible属性都为false!原来,我们平常看到的帮助中的示例都是prg文件,在这些文件中用addobject()方法向表单添加的对象在显示表单后都是可见的。而在表单的scx文件中使用addobject()方法建立的任何东西,其visible属性都为false!

本质上,用createobject()、addobject()方法建立的对象,其实只是在内存中建立了一个对象变量,必须再用语句使它们实例化。比如,我们常用的mainform.show语句就是如此,没有使用show方法,mainform就只是内存中的一个变量,而不是一个表单不可见的实例!反之,thisform.release语句则从根本

上释放了表单上所有的对象变量,从而在内存中完全清除了表单.

而在prg文件中与scx文件中用addobject()方法建立表单和控件的顺序是不一样的,在prg文件中是先向表单添加控件再显示表单,表单上的控件继承了表单的visible属性,当表单实例化时控件也实例化;而在scx文件使用addobject()方法是先显示表单,然后再向表单添加控件的,因此必须手工设置控件的visible属性为Ture。

当然这样比较麻烦,干脆在mybut类库中手工建立一个Resultform表单类,在该表单类上添加一个命令按钮cmdReturn和一个Grid1,设置命令按钮cmdReturn的caption属性为"返回",Click事件代码为thisform.release,设置Grid1的RecordSourceType属性为1-别名,RecordSource属性为Tempview,这样就

不用在代码中手工输入thisformset.form1.cmdReturn1.visible=.t.语句,省事多了。

最终的程序代码如下:

*******************************************************

set database to databasename

 

if used("tempview")

rename view tempview to oldview

delete view oldview

endif

 

Create sql view tempview as &sqlstatement

=requery()

IF _TALLY = 0

#DEFINE MSG_LOC "没有找到符合条件的纪录!"

#DEFINE TITLE_LOC "没有找到纪录"

=MESSAGEBOX(MSG_LOC,64 0 0,TITLE_LOC)

ELSE

set Classlib to mybut additive

thisformset.addobject("form1","Resultform")

thisform.hide

thisformset.form1.show

Endif

********************************************************

运行程序,一切ok!终于实现了动态生成查询条件,动态显示结果。只要再用dbsetprop()语句设置视图为可更新,就能对查询结果进行修改/更新了!这是我见到过的最完美的复合查询了!就这么简单?没错,就这么简单!一个困扰数月的问题,研究的时候峰回路转,最终结果却是如此轻松!这就是编程的艺术吧!
这个问题的解决,虽然走了许多弯路,但是也让我们了解了许多VFP的原理,难道不是很值得吗?!光凭书本的知识,你永远也无法了解这些东西的。有人说:读三年的书,还不如写一个月的程序,不是吗?!其他:

我在本文中省略了开头的动态生成sql语句的部分,具体的做法大家可以研究一下VFP自带的示例应用程序solution中databases目录下的view/queries目录中的Interactively Bulid a sql statement示例,你甚至可以稍作修改就在你自己的程序中使用该表单。

我曾经就其原理写了一篇文章《交互式建立sql复合查询-vfp示例应用程序详解》贴在网易虚拟社区VFP版,有兴趣的朋友可以在精华区查到,读完该篇文章,你应该可以自行修改该程序使之能够对一对多数据库进行查询了。因为该文篇幅很长,又比较枯燥,就不在这儿解说了。

要注意的是:原示例程序用于生成sql语句查询。要改为用于建立sql视图,必须作一些修改:

1、在sql查询中不必限定表别名和数据库名,而建立sql视图却必须这样做。因此需要修改makesql表单的

自定义方法bldsql的代码,将源代码下面的部分:

**************************************************************************

IF !EMPTY(lcOperand)

lcValue2 = THISFORM.ValidateType(THIS.cboField2.Value,lcValue2)

lcWHERE = lcOperand " " lcField2 " " ;

lcRelation2 " " lcValue2

ENDIF

** Create the first part of the WHERE condition

lcWHERE = "WHERE " lcField1 " " lcRelation1 " " lcValue1 " " lcWHERE

 

** Create the full SQL command using the base table for the form

lcSQL = "SELECT * FROM " lcAlias " " lcWHERE

****************************************************************************

修改为:

****************************************************************************

If !empty(lcOperand)

lcValue2 = thisform.ValidateType(this.cboField2.value,lcValue2)

lcWhere = lcOperand " " lcAlias "." lcField2 " " ;

lcRelation2 " " lcValue2

Endif

lcWhere = "Where " lcAlias "." lcField1 lcRelation1 " ";

lcValue1 " " lcWhere

lcSql = "Select * From " "DatabaseName!" lcAlias " " lcWhere

****************************************************************************

DatabaseName是你的数据库的名字。以上修改的实质是,给要查询的字段名限定其所在的表别名,给

select form的表别名限定所属的数据库。

2、修改RunSql命令按钮的Click事件代码,将原代码:

*************************************************************************

cMacro = ALLTRIM(THISFORM.edtSQL.Value) "INTO CURSOR TEMPQUERY"

*************************************************************************

中的 ( "INTO CURSOR TEMPQUERY")部分删除,将cMacro改为sqlstatement.并将除了下面部分外的全

部代码删除:

*************************

IF USED(lcOldAlias)

SELECT (lcOldAlias)

ENDIF

*************************

,插入上面的最终代码就可以了。

ok!现在所有的任务都完成了。做完所有的这一切,花不了十分钟,你就建立了一个强大的复合查询!
推荐阅读
  • 推荐一个ASP的内容管理框架(ASP Nuke)的优势和适用场景
    本文推荐了一个ASP的内容管理框架ASP Nuke,并介绍了其主要功能和特点。ASP Nuke支持文章新闻管理、投票、论坛等主要内容,并可以自定义模块。最新版本为0.8,虽然目前仍处于Alpha状态,但作者表示会继续更新完善。文章还分析了使用ASP的原因,包括ASP相对较小、易于部署和较简单等优势,适用于建立门户、网站的组织和小公司等场景。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了使用postman进行接口测试的方法,以测试用户管理模块为例。首先需要下载并安装postman,然后创建基本的请求并填写用户名密码进行登录测试。接下来可以进行用户查询和新增的测试。在新增时,可以进行异常测试,包括用户名超长和输入特殊字符的情况。通过测试发现后台没有对参数长度和特殊字符进行检查和过滤。 ... [详细]
  • 本文详细介绍了MysqlDump和mysqldump进行全库备份的相关知识,包括备份命令的使用方法、my.cnf配置文件的设置、binlog日志的位置指定、增量恢复的方式以及适用于innodb引擎和myisam引擎的备份方法。对于需要进行数据库备份的用户来说,本文提供了一些有价值的参考内容。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文由编程笔记小编整理,介绍了PHP中的MySQL函数库及其常用函数,包括mysql_connect、mysql_error、mysql_select_db、mysql_query、mysql_affected_row、mysql_close等。希望对读者有一定的参考价值。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • MyBatis错题分析解析及注意事项
    本文对MyBatis的错题进行了分析和解析,同时介绍了使用MyBatis时需要注意的一些事项,如resultMap的使用、SqlSession和SqlSessionFactory的获取方式、动态SQL中的else元素和when元素的使用、resource属性和url属性的配置方式、typeAliases的使用方法等。同时还指出了在属性名与查询字段名不一致时需要使用resultMap进行结果映射,而不能使用resultType。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
author-avatar
手机用户2602892387
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有