热门标签 | 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!现在所有的任务都完成了。做完所有的这一切,花不了十分钟,你就建立了一个强大的复合查询!
推荐阅读
  • PHP 5.2.5 安装与配置指南
    本文详细介绍了 PHP 5.2.5 的安装和配置步骤,帮助开发者解决常见的环境配置问题,特别是上传图片时遇到的错误。通过本教程,您可以顺利搭建并优化 PHP 运行环境。 ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • 在当前众多持久层框架中,MyBatis(前身为iBatis)凭借其轻量级、易用性和对SQL的直接支持,成为许多开发者的首选。本文将详细探讨MyBatis的核心概念、设计理念及其优势。 ... [详细]
  • 在使用 DataGridView 时,如果在当前单元格中输入内容但光标未移开,点击保存按钮后,输入的内容可能无法保存。只有当光标离开单元格后,才能成功保存数据。本文将探讨如何通过调用 DataGridView 的内置方法解决此问题。 ... [详细]
  • 本文详细介绍了如何在 Linux 平台上安装和配置 PostgreSQL 数据库。通过访问官方资源并遵循特定的操作步骤,用户可以在不同发行版(如 Ubuntu 和 Red Hat)上顺利完成 PostgreSQL 的安装。 ... [详细]
  • 如何在PostgreSQL中查看数据表
    本文将指导您使用pgAdmin工具连接到PostgreSQL数据库,并展示如何浏览和查找其中的数据表。通过简单的步骤,您可以轻松访问所需的表结构和数据。 ... [详细]
  • 利用存储过程构建年度日历表的详细指南
    本文将介绍如何使用SQL存储过程创建一个完整的年度日历表。通过实例演示,帮助读者掌握存储过程的应用技巧,并提供详细的代码解析和执行步骤。 ... [详细]
  • 本文介绍了如何通过 Maven 依赖引入 SQLiteJDBC 和 HikariCP 包,从而在 Java 应用中高效地连接和操作 SQLite 数据库。文章提供了详细的代码示例,并解释了每个步骤的实现细节。 ... [详细]
  • 在使用SQL Server进行动态SQL查询时,如果遇到LIKE语句无法正确返回预期结果的情况,通常是因为参数传递方式不当。本文将详细探讨这一问题,并提供解决方案及相关的技术背景。 ... [详细]
  • 本文介绍如何通过创建替代插入触发器,使对视图的插入操作能够正确更新相关的基本表。涉及的表包括:飞机(Aircraft)、员工(Employee)和认证(Certification)。 ... [详细]
  • MySQL缓存机制深度解析
    本文详细探讨了MySQL的缓存机制,包括主从复制、读写分离以及缓存同步策略等内容。通过理解这些概念和技术,读者可以更好地优化数据库性能。 ... [详细]
  • SQLite 动态创建多个表的需求在网络上有不少讨论,但很少有详细的解决方案。本文将介绍如何在 Qt 环境中使用 QString 类轻松实现 SQLite 表的动态创建,并提供详细的步骤和示例代码。 ... [详细]
  • 精选30本C# ASP.NET SQL中文PDF电子书合集
    欢迎订阅我们的技术博客,获取更多关于C#、ASP.NET和SQL的最新资讯和资源。 ... [详细]
  • MySQL 数据库迁移指南:从本地到远程及磁盘间迁移
    本文详细介绍了如何在不同场景下进行 MySQL 数据库的迁移,包括从一个硬盘迁移到另一个硬盘、从一台计算机迁移到另一台计算机,以及解决迁移过程中可能遇到的问题。 ... [详细]
  • Hadoop入门与核心组件详解
    本文详细介绍了Hadoop的基础知识及其核心组件,包括HDFS、MapReduce和YARN。通过本文,读者可以全面了解Hadoop的生态系统及应用场景。 ... [详细]
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社区 版权所有