精华内容
下载资源
问答
  • SQL与关系数据库理论:如何编写健壮的SQL代码.pdf 中文版
  • SQL与关系数据库理论 如何编写健壮的SQL代码_PDF电子书下载 带书签目录 完整版.pdf
  • 如何编写高效的SQL代码,包括优化原则及经验之谈
  • 自己动手用C#编写SQL注入漏洞扫描器 关键字:SQL注入,扫描器
  • 1.sqlserver 编写脚本示例代码 --部分脚本规范 --一、创建存储过程 --1、判断数据库中是否存在要创建的存储过程,如果存在先drop掉 if exists (select * from dbo.sysobjects where id = object_id(N'[dbo]....

    1.sqlserver 编写脚本示例代码

    --部分脚本规范
    
    
    --一、创建存储过程
    
    --1、判断数据库中是否存在要创建的存储过程,如果存在先drop掉
    if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[部门计划实际对比]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
    drop procedure [dbo].[部门计划实际对比]
    GO
    
    --2、创建存储过程
    SET QUOTED_IDENTIFIER OFF 
    GO
    SET ANSI_NULLS OFF 
    GO
    
    CREATE PROCEDURE [dbo].[部门计划实际对比]	
    	(
    		@年		INT,
    		@月		INT,
    		@部门编码	VARCHAR(50)
    	) 
    AS
    
    select getdate()
    
    GO
    SET QUOTED_IDENTIFIER OFF 
    GO
    SET ANSI_NULLS ON 
    GO
    
    -------------------------------------------------------------------------------------------------------------------
    
    
    --二、创建表
    
    --判断数据库中是否存在要创建的表,如果不存在创建
    IF NOT EXISTS (SELECT * FROM DBO.SYSOBJECTS WHERE ID = OBJECT_ID(N'[DBO].[AQ证件]') AND OBJECTPROPERTY(ID, N'ISUSERTABLE') = 1)
    BEGIN
    	CREATE TABLE [DBO].[AQ证件] (
    		[证件编号] [VARCHAR] (20) COLLATE CHINESE_PRC_CI_AS NOT NULL ,
    		[工种] [VARCHAR] (20) COLLATE CHINESE_PRC_CI_AS NOT NULL ,
    		[职工编码] [VARCHAR] (20) COLLATE CHINESE_PRC_CI_AS NOT NULL ,
    		[血型] [VARCHAR] (20) COLLATE CHINESE_PRC_CI_AS NULL ,
    		[文化程度] [VARCHAR] (20) COLLATE CHINESE_PRC_CI_AS NULL ,
    		[发证时间] [SMALLDATETIME] NULL ,
    		[有效期至] [SMALLDATETIME] NULL ,
    		[最近培训时间] [SMALLDATETIME] NULL ,
    		[失效] [BIT] NULL CONSTRAINT [DF_AQ持证管理_失效] DEFAULT (0),
    		[建档人] [VARCHAR] (20) COLLATE CHINESE_PRC_CI_AS NULL ,
    		[建档日期] [DATETIME] NULL ,
    		[建档机器] [VARCHAR] (50) COLLATE CHINESE_PRC_CI_AS NULL ,
    		[变更人] [VARCHAR] (20) COLLATE CHINESE_PRC_CI_AS NULL ,
    		[变更日期] [DATETIME] NULL ,
    		[变更机器] [VARCHAR] (50) COLLATE CHINESE_PRC_CI_AS NULL ,
    		CONSTRAINT [PK_AQ持证管理] PRIMARY KEY  CLUSTERED 
    		(
    			[证件编号]
    		)
    	)
    END
    GO
    
    -------------------------------------------------------------------------------------------------------------------
    
    
    --三、创建视图
    
    --1、判断数据库中是否存在要创建的视图,如果存在先drop掉
    if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[AQ工种]') and OBJECTPROPERTY(id, N'IsView') = 1)
    drop view [dbo].[AQ工种]
    GO
    
    --2、创建视图
    SET QUOTED_IDENTIFIER ON 
    GO
    SET ANSI_NULLS ON 
    GO
    
    CREATE VIEW dbo.AQ工种
    AS
    SELECT 序号, 口径类别, 口径编码, 口径名称, 拼音
    FROM dbo.系统口径
    WHERE (口径类别 = 'AQ工种')
    
    GO
    SET QUOTED_IDENTIFIER OFF 
    GO
    SET ANSI_NULLS ON 
    GO
    
    -------------------------------------------------------------------------------------------------------------------
    
    
    --四、创建用户自定义函数
    
    --1、判断是否存在要创建的函数,如果存在先drop掉
    if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[fn_转换大写金额]') and xtype in (N'FN', N'IF', N'TF'))
    drop function [dbo].[fn_转换大写金额]
    GO
    
    --2、创建函数
    SET QUOTED_IDENTIFIER ON 
    GO
    SET ANSI_NULLS ON 
    GO
    
    
    CREATE function dbo.fn_转换大写金额
    (
    	@n_lowermoney numeric(15,2)
    )
    returns varchar(200) as 
    begin 
    	return ''
    end
    
    
    
    GO
    SET QUOTED_IDENTIFIER OFF 
    GO
    SET ANSI_NULLS ON 
    GO
    
    -------------------------------------------------------------------------------------------------------------------
    
    
    --五、表添加列
    
    --1、判断表格中是否存在要添加的列,不存在时添加
    IF (dbo.ColumnExist('物料库存初始','实际价格') = 0)
    BEGIN
    	ALTER TABLE dbo.物料库存初始 ADD 实际价格 SMALLMONEY
    END
    GO
    
    -------------------------------------------------------------------------------------------------------------------
    
    
    --六、修改表中列
    
    --1、如果存在指定的列,修改列格式
    IF (dbo.ColumnExist('物料库存初始','实际价格') = 1)
    BEGIN
    	ALTER TABLE dbo.物料库存初始 ALTER COLUMN 实际价格 MONEY
    END
    GO
    
    -------------------------------------------------------------------------------------------------------------------
    
    
    --七、删除表中列
    IF (dbo.ColumnExist('物料库存初始','实际价格') = 1)
    BEGIN
    	ALTER TABLE dbo.物料库存初始 DROP COLUMN 实际价格
    END
    GO
    
    -------------------------------------------------------------------------------------------------------------------
    
    
    --八、添加触发器
    
    --1、如果已存在要添加的触发器,先drop掉
    
    IF  OBJECT_ID('TR_跟踪材料单据_添加') IS NOT NULL   
    	DROP TRIGGER Tr_跟踪材料单据_添加
    GO
    
    --2、创建触发器
      	CREATE TRIGGER Tr_跟踪材料单据_添加 ON [dbo].[材料单据] 
    	FOR INSERT
    	AS
    	IF EXISTS(SELECT * FROM Config WHERE 参数='启用材料单据自动跟踪' AND 参数值 = '1')
    	BEGIN
    		INSERT INTO [dbo].[材料单据变动历史]([变动时间], [变动机器], [变动说明], [变动人])--, [ID], [单据ID], [申请日期], [单据编号], [部门编码], [工程编码], [仓库编码], [对方部门], [对方仓库], [费用编码], [费用名称], [规格], [厂家], [单位], [计划数量], [审批数量], [实际数量], [计划单价], [实际单价], [计划金额], [实际金额], [级别], [录入人], [录入时间], [申请人], [申请时间], [审核人], [审核时间], [打印], [确认人], [确认时间], [取消人], [取消时间], [料单状态], [发放日期], [来源], [仓库], [备注], [记帐数量], [记帐单价], [记帐金额], [记帐模式], [记帐时间], [记帐标志], [冲记帐], [费用来源], [配送地点], [原始单据], [是否以旧换新], [换新率], [应回收数量], [实回收数量], [已回收], [回收人], [回收时间], [记帐年], [记帐期], [主管审批角色], [主管审批操作员], [主管已审批], [主管审批时间], [安全投入分类], [退回人], [退回时间], [原材料编码])
    		SELECT 
    			  GETDATE()
    			, HOST_NAME()
    			, '添加'
    			, SUSER_SNAME()
    			
    		FROM INSERTED
    	END
    
    GO
    
    -------------------------------------------------------------------------------------------------------------------
    
    
    --九、添加约束
    
    --1、判断数据库中是否存在要添加的约束,不存在时添加
    IF NOT EXISTS (SELECT * FROM information_schema.CONSTRAINT_COLUMN_USAGE WHERE Table_name='物料' and constraint_name = 'FK_物料_费用类别')
    BEGIN
    	ALTER TABLE dbo.物料 ADD CONSTRAINT
    		FK_物料_费用类别 FOREIGN KEY
    		(
    		材料类别
    		) REFERENCES dbo.费用类别
    		(
    		编码
    		)
    END
    GO
    
    
    --十、添加数据
    
    if not exists (select * from Config where 参数='柱状图图表颜色序列')
    BEGIN
    	INSERT [Config] ( [序号] , [参数] , [参数值] , [备注]  ) VALUES ( 21000 , '柱状图图表颜色序列' , 'AFD8F8,F6BD0F,8BBA00,FF8E46,008E8E,D64646,8E468E,588526,B3AA00,008ED6,9D080D,A186BE,993300,003300,333399,333333,FF6600,00CCFF,FF99CC,FF0097,758A99,815476,41555D,827100,7C4b00,C93756,003472,845A33,44CEF6,6E511E', '以逗号分隔的Web16进制颜色值,最后不能有逗号,不能是中文逗号' )
    END
    go
    
    
    
    
    

     

    展开全文
  • Delphi代码编写SqlServer数据库备份还原工具,可直接还原sql server 的 mdf ldf 文件,直接还原备份文件。
  • 高效SQL代码编写 针对开发人员使用 很好的很详细
  • SQL一些精彩的SQL代码

    2009-03-01 10:39:07
    为一名programmer,积累代码是很重要的。下面是我从网上搜集得到的,可以作为平常编写SQL代码时做参考之用。
  • 建库,建表,添加记录,创建军存储过程等其他SQL代码编写
  • 本书主要介绍了关系数据库的原理与SQL的内在关系,阐述了该如何把关系理论直接应用到SQL的应用中,并通过大量的例子以及对其背后理论解释,帮助读者编写出高效而精确的SQL代码。全书由12章和4个附录组成,每章后面都...
  • 支持任意合法的表达式,()+-*/ 任意组合成表达式进行计算, 这不是网上留传的调用COM计算的版本(那种速度慢,又受环境影响), 而是本人硬着头皮用原生SQL代码开发的,估计您在网上也找不到,找到了也不是原生SQL编写的.
  • TeradataSQL编写规范

    2018-08-11 11:40:19
    为了统一软件开发过程中关于SQL...式,使编码人员编写SQL 代码遵从特定的风格,并养成良好的开发习惯,从而增 强代码的可读性,便于交流和维护,特此收集、整理公司已经积累的技术资料, 修订和编制了本编码规范。
  • SQL数据库代码编写器(c#)能很方便的生成你需要的SQL查询语句能很方便的生成你需要的SQL查询语句
  • 使用sql语句定义一个存储过程挺简单的,参考这一篇博文https://blog.csdn.net/qq_44973159/article/details/91491985 create procedure ccgc as select * from t_student 而使用java并不 那么简单了,,,如下 ...

    1.定义一个存储过程(不带参数)

    使用sql语句定义一个存储过程挺简单的,参考这一篇博文https://blog.csdn.net/qq_44973159/article/details/91491985
    create procedure ccgc as select * from t_student
    而使用java并不 那么简单了,,,如下

    package java617存储过程;
    
    import java.sql.*;
    
    //定义一个不带参数的存储过程
    public class test {
    	private static ResultSet rs=null;
    	private static Connection dbConn=null;
    	private static PreparedStatement stmt=null;
    	
    	
    	public static void main(String[] args) {
    	String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
    	String dbURL = "jdbc:sqlserver://localhost:1433 ;Databasename =studentDB;"
    										+"integratedSecurity=true";
    		String sql ="create procedure ccgc" + " as select * from t_student" ;
    		//在这里定义好存储过程
    		try{
    			Class.forName(driverName);
    			dbConn = DriverManager.getConnection(dbURL); //连接url
    			stmt =dbConn.prepareStatement(sql);   //将存储过程写入到数据库
    			stmt.executeUpdate();      //对数据库的信息进行更新
    			System.out.println("连接完成");
    		}
    		catch (Exception e){
    			System.out.println("连接未完成");
    			e.printStackTrace();
    		}
    		
    	}
    }
    
    

    出现过的问题:在直接用连接sql语句中的 DriverManager.getConnection(dbURL); 在编译运行的过程中报错,空指针异常
    在进sql server查看这个存储过程保存与否;
    在这里插入图片描述


    2.调用一个存储过程(不带参数)

    定义存储过程后,怎么调用呢?用sql语句很简单,
    在这里插入图片描述
    直接exec+name即可,那用java呢?如下:

    package java617存储过程;
    //调用不带参数的存储过程
    import java.sql.*;
    
    public class testDemo {
    	private static ResultSet rs=null;
    	 private static Connection dbConn=null;
    	 private static PreparedStatement stmt=null;
    	 //======对所有的值进行初始化,避免空指针异常======
    	
    	public static void main(String[] args) {
    		String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
    		String dbURL = "jdbc:sqlserver://localhost:1433 ;Databasename =studentDB;"
    											+"integratedSecurity=true";
    			//String sql ="create procedure ccgc" + " as select * from t_student" ;
    		 	String sql = "{call ccgc} ";  //  ====  调用存储过程  ==== 
    			try{
    				Class.forName(driverName);
    				dbConn = DriverManager.getConnection(dbURL);
    				//stmt =dbConn.prepareStatement(sql);
    				
    				CallableStatement cstmt = dbConn.prepareCall(sql);                            //===调用sql语句(调用存储过程的语句)
    				rs = cstmt.executeQuery();  // === 接收sql返回的内容
    				 while (rs.next()) {
    					    System.out.print("学号"+rs.getString(1));
    					    System.out.print("姓名"+rs.getString(2));
    					    System.out.print("性别"+rs.getString(3));
    					    System.out.print("出生日期"+rs.getString(4));
    					    System.out.println("班级编号"+rs.getString(5));
    					    //======  遍历数据库中的数据并打印输出 =====
    					    // === 在这输出语句可以用一条输出语句输出,单一出口原则===
    					   }
    				System.out.println("连接完成");
    			}
    			catch (Exception e){
    				System.out.println("连接未完成");
    				e.printStackTrace();
    			}
    
    	}
    }
    
    

    在这里插入图片描述


    3.定义一个存储过程(含参数)

    package java617存储过程;
    
    import java.sql.*;
    
    //============定义一个带参数的存储过程===========
    
    public class test1 {
    	private static ResultSet rs=null;
    	 private static Connection dbConn=null;
    	 private static PreparedStatement stmt=null;
    	
    	
    	public static void main(String[] args) {
    	String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
    	String dbURL = "jdbc:sqlserver://localhost:1433 ;Databasename =studentDB;"
    										+"integratedSecurity=true";
    		String sql ="create proc ccgc3 @sno char(10) " + 
    					" as select cno,grade,sno from t_score where sno =@sno" ;
    		try{
    			Class.forName(driverName);
    			dbConn = DriverManager.getConnection(dbURL);
    			stmt =dbConn.prepareStatement(sql);
    			stmt.executeUpdate();
    			System.out.println("连接完成");
    		}
    		catch (Exception e){
    			System.out.println("连接未完成");
    			e.printStackTrace();
    		}
    		
    	}
    }
    
    

    我们只需要将不带参数的存储过程的代码,把定义存储过程的sql语句改成带参的语句就ok了;
    在这里插入图片描述


    4.调用存储过程(含参数)

    展开全文
  • SQL代码编写

    千次阅读 2019-10-11 09:07:04
    代码编写在JAVA、C#、PHP中都比较常见,但在SQL里面似乎用的比较少,如果能把它应用到SQL里面的话,对于复杂SQL语句的编写会有很大帮助。这里拿一个较为常见的SQL来举例,这个表格在很多面试的时候都有遇到过:  ...

      伪代码编写在JAVA、C#、PHP中都比较常见,但在SQL里面似乎用的比较少,如果能把它应用到SQL里面的话,对于复杂SQL语句的编写会有很大帮助。这里拿一个较为常见的SQL来举例,这个表格在很多面试的时候都有遇到过:
      有两张表格,分别是学生信息表(Student)、分数表(Score),表格设计如下:

     

    要求:取出每个人的数学、语文、物理三科的成绩,结果行列设计如下:

    姓名  数学  语文  物理

    张三  80   80   80  

    李四  80   80   80

    王五  80   80   80

      以上为了方便起见,就没有按数据库里面的实际数据来写,大家知道是什么意思就行了。

      这个问题是我早年参加笔试时的梦魇,因为总是想不到该怎么写。但后来在一家互联网公司里混,整天碰到的都是这类东西,写的多了也就无所谓了。其实这就是伪代码的一个应用,下面作个示例。

      表格设计良好的情况下,我们应该是这样写语句的:

    SELECT st.StudentName AS 姓名,ISNULL(math.Score,0) AS 数学,ISNULL(chn.Score,0) AS 语文,ISNULL(phy.Score,0) AS 物理

    FROM Student st

        LEFT JOIN Score_Math math ON math.StudentId=st.StudentId---关联数学表

        LEFT JOIN Score_Chinese chn ON chn.StudentId=st.StudentId--关联语文表

        LEFT JOIN Score_Physic phy ON phy.StudentId=st.StudentId--关联物理表

      这个代码一看就知道是有问题的,因为数学、语文、物理三个表格并不存在,如果我们能够把这三张表格给构建起来,依次代替Math、Chinese、Physic这三张表的位置,这个SQL就算是完成了。

      首先构建Math表:

      SELECT Score,StudentId FROM Score WHERE SubjectName='数学'

      然后用构建好的Math表去替换伪代码中的数学表:

    SELECT st.StudentName AS 姓名,ISNULL(math.Score,0) AS 数学,ISNULL(chn.Score,0) AS 语文,ISNULL(phy.Score,0) AS 物理

    FROM Student st

        LEFT JOIN (SELECT Score,StudentId FROM Score WHERE SubjectName='数学')math ON math.StudentId=st.StudentId---关联数学表

        LEFT JOIN (SELECT Score,StudentId FROM Score WHERE SubjectName='语文')chn ON chn.StudentId=st.StudentId--关联语文表

        LEFT JOIN (SELECT Score,StudentId FROM Score WHERE SubjectName='物理')phy ON phy.StudentId=st.StudentId--关联物理表

      大功告成!

     

      当然还有另外一种写法,这种写法就是通过过滤数据来构建数学\语文\物理三个分数表,只是这种写法看上去不是很直观,不过也算一种方法,我也把它写出来吧:

    SELECT st.StudentName AS 姓名,ISNULL(math.Score,0) AS 数学,ISNULL(chn.Score,0) AS 语文,ISNULL(phy.Score,0) AS 物理

    FROM Student st

     LEFT JOIN Score math ON math.StudentId=st.StudentId AND math.SubjectName='数学' ---关联数学表
     LEFT JOIN Score chn ON chn.StudentId=st.StudentId AND chn.SubjectName='语文'--关联语文表
     LEFT JOIN Score phy ON phy.StudentId=st.StudentId AND phy.SubjectName='物理'--关联物理表

     

    看下运行结果:

     

      总结一下,SQL伪代码的思路有以下几步:
      1、先按照题目中的要求,用SELECT 字段 FROM 表格关联 WHERE 条件的方法,构建出一个最初的SQL语句。这个语句中仅包含必须提取的字段,以及提取这些字段所需要的表格(不存在的话就自己命名一个),这个就是我们需要的伪代码
      2、查看语句中不存在的字段与表格,通过临时表构建、数据筛选的方式建立关联表,然后把它替代到伪代码里面,结束。

      当然,实际的SQL编写可能比这复杂的多,不过都可以通过类似的方法来构建,只是构建层级更多一些而已

    展开全文
  • 酒店管理系统,C#源代码编写,SQL Server数据库综合实例代码.
  • SQL代码助手

    2013-03-14 14:31:53
    SQL.Assistant.v4.6.12,一款很强大的SQL代码助手,可以用于SQL中编写过程格式化代码,也可以用于没安装SQL,用记事直接查询数据库,很强大的小插件。
  • Sql与关系数据库理论:如何编写健壮的sql代码(原书第2版) - (美)c.j.date.epub
  • 如何根据动态SQL代码自动生成DTO

    千次阅读 2016-06-29 16:21:23
    一般做数据库相关开发,总免不了需要手工编写SQL代码,甚至还需要根据参数来动态拼接SQL。各种框架基本上都有一套自己拼接动态SQL的方案,也都能很轻松的将查询出来的数据转换为对象(DTO)。 不过到目前为止,这些...

    当前的状况

    一般做数据库相关开发, 除非学习, 否则很少有人愿意直接使用JDBC。本来Java代码就比较啰嗦了,而直接用JDBC写代码之啰嗦简直有些令人发狂!所以在实际开发过程中,我们通常都会使用一些框架/库来帮助我们操作数据库。而且开源市场上的选择也比较多,就我个人接触到的有:Hibernate,MyBatis,JdbcTemplate,DbUtils,ActiveRecord,JavaLite等等。 这些框架都能大幅的提高开发效率,对于一些基本CRUD操作来说,虽然各有差异,但总的来说基本是够用了。

    然而对于稍微复杂点的数据查询来说,总免不了需要手工编写SQL代码,甚至还需要根据参数来动态拼接SQL。各种框架基本上都有一套自己拼接动态SQL的方案,也都能很轻松的将查询出来的数据转为对象(DTO)。

    不过到目前为止,这些框架虽然能够很轻松的帮助我们完成数据的映射,但是这些DTO还得需要我们手工一个个的去编写。

    存在的问题

    通常我们在写完SQL的查询代码后, 需要有一个对应的DTO,将数据库中查询出的数据映射到DTO,以便于调用的程序能够更好的使用这些数据。当然,为了省事,有时也会把数据直接存储在像Map这样的数据结构中。不过, Map这种方式虽然很轻便,但是会带来几个比重要的潜在问题:

    • 调用者需要记住Map里面每个key的名称,这就会给程序员带来一些所谓的记忆负担
    • 过重的记忆负担,就会导致系统的逻辑复杂,理解困难,维护更困难
    • SQL更改导致Key发生变化后,很难发现问题,需要程序员非常小心的处理这些更改

    如果想要避免Map带来的这些问题,我们需要为每个SQL查询都单独编写DTO。尽管书写这些DTO并没有什么难度,但是非常枯燥乏味,特别是字段很多的时候更是如此;并且,如果SQL查询的字段出现更改,也还是要记得回来修改这个DTO。单独编写DTO虽然减轻了Map带来的部分问题,同时也额外增加了新的工作量。

    如果有一种方法能够在SQL代码(包括动态拼接的SQL)编写完成后,就自动的做到下面2点就非常完美了:

    1. 根据SQL代码,直接生成对应的DTO
    2. 变更SQL代码,自动修改对应的DTO

    这样,一方面解决了手工书写DTO的麻烦; 另一方面,当修改SQL导致某个字段发生更改时, 由于自动生成的DTO也会同步修改,在那些引用到这个字段的地方,编译器就会立即给出错误提示! 使得问题一产生就能立即被发现,这样可以避免了很多潜在的问题。

    本文正是试图要解决如何根据SQL代码自动生成DTO的问题,省去手工编写的麻烦,提高程序员的开发效率。

    解决的思路

    理想总是很美好,现实总是很残酷!

    那么,到底能否实现这个想法呢,我们首先来初步分析一下自动产生DTO的可行性:

    要实现自动产生DTO,其核心就是要拿到SQL查询所对应的每个列名及其数据类型。有了列名和数据类型,就能很容易写一个方法来产生DTO了。

    我们知道,在一般情况下,SQL查询写完之后,包括调用存储过程和那些根据调用参数来动态拼接的SQL,虽然最终运行的SQL可能不尽相同,但是其查询结果的字段部分都是相对固定的。

    当然,也有极少情况下会碰到字段都不确定的查询,不过在这种极端情况下,即使手工也没法写DTO了,反倒是用Map更合适, 我们这里不做讨论。

    那么,怎么才能拿到列名和类型呢?

    一种方案是分析SQL代码中SELECT部分的字段,不过其局限性比较大:

    • 对于拼接的SQL代码,分析难度比较大
    • 字段的类型也难以判断
    • SELECT * …; CALL statement 这样常见的查询方式分析起来难度也很大

    上述方案对像Mybatis这种采用配置文件(xml)来写SQL的方式,似乎有些可行性,我没有具体试验过,但估计面临的困难不会少。

    另一种方案是想办法直接运行包含SQL的这些代码:

    我们知道JDBC执行一个SQL查询,会返回ResultSet对象,通过该对象中的方法getMetaData(),能够得到这次查询的一些元数据:如列名称,列类型,以及该列所在的表名等,这些信息就已经足够我们来产生需要的那个类了。

    那么,怎么才能够运行这些包含SQL的代码呢?

    对于那些固定的SQL语句还稍微好说点,我们拿到这个固定的SQL,调用JDBC就能拿到MetaData,然后就可以很容易的根据这些信息来生成DTO。但是,对于那些复杂的需要根据一系列参数来动态产生的SQL查询,在参数设置好前是无法直接运行的,也就无法得到MetaData,得不到MetaData我们就无法生成DTO。

    怎么办?

    前面已经讨论了,即便是动态SQL,无论输入什么样的参数,虽然执行的SQL语句可能不一样,但是最终产生结果列却是固定的。 我们当前需要解决的问题不正是要获取这些列信息吗? 既然如此,那我们就构造一系列默认的参数值。这些参数并没有实际用处,仅仅是为了让我们正在编辑SQL代码得以正常运行,以便拿到需要的MetaData,至于能否查询到数据并不紧要。

    通常我们编写的SQL代码,有2种存在形式:一是直接在Java代码中, 另外一种是放在配置文件中。这里不讨论哪种形式更好,以后我会单独再找地方来讨论。这里主要讨论的是在Java代码中拼接的SQL, 如何实现一个代码生成器来自动生成这些DTO:

    要全自动化的解决这个问题,我们先来看看这个代码生成器所要面临的一些挑战及应对的思路:

    • 如何标识一段需要生成DTO的SQL代码

    首先,我们需要标识出这段代码,以便于代码生成器可以运行这段需要生成DTO代码。而通常情况下,我们的数据接口都是方法级别的,因此我们可以通过对方法进行注解,用注解来标识这个方法要返回一个DTO对象是个不错的选择。

    • 如何定义DTO的类名

    一种很容易想到的方法就是通过SQL代码所在的类名+方法名自动组合出一个名称, 当然有时为了灵活控制,应该允许程序员指定一个名字。

    • 如何执行代码

    执行代码的关键是构造一批能够调用注解方法的合适参数。当然首先需要对注解的方法进行代码分析,提取方法参数名及类型。代码分析可以用类似JavaCC这样的工具,或者一些语法分析器,这里不做细究。下面主要探讨下默认参数的构造:

    为了简化问题,默认情况下我们可以按如下规则进行构造:

    数字型参数,默认为:0, 例如:public Object find(int arg){...} 构造 int arg=0;  
    字符串参数,默认为:"",     构造 String arg="";  
    布尔型参数,默认为:false,  构造 boolean arg=false;  
    数组型参数,默认为:类型[0], 构造 int[] arg=new int[0];  
    对象型参数,默认为:new 类型(), 例如:public Object find(User arg){...} 构造 User arg=new User();  
    

    当然,对于一些简单参数的情况下,上面构造规则基本上都能够奏效。 但是,对于有些参数:比如参数是一个接口,或者是一个需要动态连接的表名,又或者是SQL拼接代码的逻辑要求参数必须是某些特殊值等等,默认构造出的参数就会导致程序无法执行。

    但是,怎么才能够让我们的代码生成器能够继续执行下去呢? 好像确实没有什么能自动处理的办法,只好把这个问题交给程序员来处理了,让程序员来帮助代码生成器完成参数的初始化。

    我们可以在注解上提供一个参数, 该参数主要完成对默认规则下无法初始化的参数进行设置。 当然,这个参数中的初始化代码也可以覆盖默认规则,以便于我们在编辑阶段就可以测试执行不同的SQL流程。

    • 如何生成DTO

    经过以上一系列的处理,我们终于能自动的把包含SQL查询代码的方法运行起来了。不过,现在我们还没得到想要的MetaData,还无法生成DTO。

    一种可能的方式是包装一个JDBC,截获本次方法调用时执行的SQL查询, 但面临的问题是,如果方法中有多次查询就比较麻烦了。

    另一种方式依赖于框架的支持,可以截获到方法的return语句,获取其执行的SQL语句, 有了SQL语句,生成DTO就没有什么难度了。

    • 如何修改代码

    为了尽量减少程序员的工作,我们的代码生成器在生成完DTO后, 还需要将方法的返回值自动修改成这个DTO类。

    • 如何处理SQL的变更

    简单的做法是:一旦有某个SQL代码发生变化,就把所有的DTO都按照前面的方法重新生成一遍。 不过,很显然当查询方法很多的时候,DTO代码生成的过程将缓慢到难以忍受。

    另外一种更合理的做法是:我们在生成DTO时增加一个指纹字段,其值可以用SQL代码中所包含的信息来产生,例如:代码长度+代码的hashCode.代码生成器在决定是否需要处理这个方法前,先计算该方法的指纹和存在于DTO里面的指纹进行比较,如果相同就跳过,否则就认为本方法的SQL发生了变更,需要更新DTO。

    具体的实现

    到此为止,基本上DTO代码生成器的主要障碍都有了相应的处理办法。最后,我们用一个具体的实现来做个简单示例。

    这里需要引入2个项目:

    这是一个功能强大且非常容易使用的ORM框架,通过@DB(jdbc_url,username,password)注解来引入数据库。

    这是一个相应的Eclipse插件,它可以:

    1. @DB注解的接口,在文件保存时 ,自动生成表的CRUD操作
    2. @Select注解的方法,在文件保存时 ,自动生成DTO
    3. 很轻松的书写多行字符串

    插件安装和设置可以参考: https://github.com/11039850/monalisa-orm/wiki/Code-Generator

    下面是一个根据动态SQL自动生成DTO示例,完整的例子工程可以参考: https://github.com/11039850/monalisa-example

        package test.dao;
    
        public class UserBlogDao {
            //@Select 注解指示该方法需自动生成DTO
            //默认类名: Result + 方法名, 默认包名:数据访问类的包名+"."+数据访问类的名称(小写)
            //可选参数:name 指定生成结果类的名称,如果未指定该参数,则采用默认类名
            //可选参数:build 初始化调用参数的Java片段代码,替换默认的参数构造规则
            @Select(name="test.result.UserBlogs") 
    
            //!!! 保存后会自动修改该函数的返回值为: List -> List<UserBlogs>
            //第一次编写时,由于结果类还不存在, 为了保证能够编译正常,
            //函数的返回值 和 查询结果要用 泛值 替代, 保存后,插件会自动修改.
            //函数的返回值 和 查询结果 泛值的对应关系分三类如下:
            //1. List查询
            //public DataTable   method_name(...){... return Query.getList();   }    或
            //public List        method_name(...){... return Query.getList();   }    
            //
            //2. Page查询
            //public Page   method_name(...){... return Query.Page();      }
            //
            //3. 单条记录
            //public Object method_name(...){... return Query.getResult(); }
            //
            public List  selectUserBlogs(int user_id){ 
                Query q=TestDB.DB.createQuery();
    
                q.add(""/**~{
                    SELECT a.id,a.name,b.title, b.content,b.create_time
                        FROM user a, blog b   
                        WHERE a.id=b.user_id AND a.id=?
                }*/, user_id);  
    
                return q.getList(); 
            } 
        }

    上述代码保存后,插件就会自动生成一个DTO类:test.result.UserBlogs, 并自动将方法修改成如下的声明:

            public List<UserBlogs>  selectUserBlogs(int user_id){ 
                ...
                return q.getList(UserBlogs.class); 
            }

    当然,如果对selectUserBlogs方法做了任何的修改(包括只是加了一个空格),保存文件后,插件也会自动更新UserBlogs。

    同时,为了方便我们调试,插件也会在Eclipse的控制台窗口输出类似下面的信息:

    2016-06-27 17:00:31 [I] ****** Starting generate result classes from: test.dao.UserBlogDao ******   
    2016-06-27 17:00:31 [I] Create class: test.result.UserBlogs, from: [selectUserBlogs(int)]
    SELECT a.id,a.name,b.title, b.content,b.create_time
        FROM user a, blog b    
        WHERE a.id=b.user_id AND a.id=0
    

    顺便补充一下:

    在Java代码中书写SQL,非常令人讨厌的一件事情就是Java语言中字符串的连接问题。使得大段的SQL代码中间要插很多的换行/转义符号,写起来很麻烦,看着也不舒服。monalisa-eclipse插件顺便也解决了多行字符串的书写问题。

    例如:

        System.out.println(""/**~{
            SELECT * 
                FROM user
                WHERE name="zzg"
        }*/);

    将会输出:

        SELECT * 
            FROM user
            WHERE name="zzg"

    当然,为了快速书写,可以在Eclipse中把多行字符串的语法设置为一个代码模板。关于多行语法的更多细节可以参考: https://github.com/11039850/monalisa-orm/wiki/Multiple-line-syntax

    到这里,动态SQL代码自动生成DTO的思路和实现例子基本上就介绍完了, 欢迎大家提出各种有理无理的意见,一起讨论、进步,谢谢!

    展开全文
  • sql的源代码,解决sql练习的难处!!!!!!!!!!!!!!!!!!!!!
  • 含源码,delphi7 编写sql代码机,半自动生成CRUD代码,支持Oracle,mysql,java;
  • sql代码开发规范文档

    2016-12-19 11:23:49
    sql代码开发规范文档
  • VB编写的利用程序附加SQL Server数据库程序,VB6.0源代码编写
  • 但是,当Java开发人员编写SQL语句时,一切都变得不同了。SQL是一种说明式语言,与面向对象思想和命令式思想无关。在SQL语言中,查询非常容易表达。但它也不是那么容易以最佳或最正确地方式编写出来。开发人员不仅...
  • PostgreSQL运行SQL代码-方法+源代码。PostgreSQL运行SQL代码-方法+源代码
  • 本系统为课程设计大作业编写的,应用了SQL vc语言 功能强大 既可以作为应急只用, 又不失为学习的范例 希望对大家有所帮助
  • 采用VB6.0编写SQL SERVER 2000数据库的备份与恢复源代码
  • SQL代码编写好数据库并且保存为sql文件后如何正确的打开?比如打开这样类型的文件 之前我都是在文件夹里之间双击打开,然后运行,总提示说什么偏移量问题,打开失败什么的。 今天试了一下好像是打开的方式不对...
  • C#+sql编写的学籍管理系统代码。注意开启sql的服务后在运行调试
  • 本系统为数据库课程设计大作业时所编写 应用了 VC加SQL编写 报告完整齐全 既可以作为应急之用 也不失为日后学习的范例 希望对大家有所帮助

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 323,325
精华内容 129,330
关键字:

编写sql代码