`
datamachine
  • 浏览: 163465 次
社区版块
存档分类
最新评论

简化SQL有序计算示例

    博客分类:
  • DB
阅读更多

       SQL集合无序使得完成有序计算比较困难,而这类计算在实际业务中又非常常见,网上有很多讨论,比如:

       http://bbs.csdn.net/topics/390959904

       http://bbs.csdn.net/topics/390994046

       http://bbs.csdn.net/topics/390980889

       http://bbs.csdn.net/topics/390998839

       http://bbs.csdn.net/topics/390813196

       http://bbs.csdn.net/topics/390977682

       http://bbs.csdn.net/topics/391020551

       http://bbs.csdn.net/topics/390976752

       http://bbs.csdn.net/topics/391028980

      

   SQL在完成这类运算时往往采用如下手段:

   1、使用窗口函数

   对于支持窗口函数的数据库(如Oracle MSSQL),使用窗口函数多数情况下可以简化SQL写法,但需要数据库支持,使用有一定局限。

         2、使用复杂SQL

 

不支持窗口函数的数据库往往要编写复杂SQL,经常要嵌套多层子查询才能完成,书写十分复杂。其中,支持变量定义,按顺序计算(如MySQL)可以简化部分计算。

        3、编写存储过程

一些比较复杂的场景,就需要在存储过程中一步步来完成,实现并不简单。

 

       如果主程序是Java的,可以使用免费的集算器来协助,集算器支持有序计算,比SQL要简单许多,下面来看一些具体实例。

同比环比计算

        行间计算(如比上期、比同期)在不支持窗口函数的数据库下实现尤为困难,需要更换思路使用连接(join)运算替代,不仅难以理解而且效率低下。即使可以使用窗口函数仍要面临嵌套子查询等问题,SQL语句仍较为冗长。

        来看集算器的解法,表sales存储着多年的订单数据,请根据该表计算出指定时间段内各月份的销售额比上期和同期比。部分源数据如下:

OrderID

Client

SellerId

Amount

OrderDate

1

WVF Vip

5

440

2/3/2009

2

UFS Com

13

1863.4

7/5/2009

3

SWFR

2

1813

7/8/2009

4

JFS Pep

27

670.8

7/8/2009

5

DSG

15

3730

7/9/2009

6

JFE

10

1444.8

7/10/2009

7

OLF

16

625.2

7/11/2009

8

PAER

29

2490.5

7/12/2010

9

DY

20

517.8

7/15/2010

10

JDR

17

1119.9

7/16/2010

11

WBG

17

2018.6

7/17/2009

12

GCD

233

100.8

7/18/2010

13

JKD

11

1746.2

7/19/2009

 

 

        集算器脚本:



 

        A1:按时间段从数据库查询数据,并按OrderDate的年月分组汇总Amount,begin和end是外部参数,比如begin="2011-01-01 00:00:00",end="2014-07-08 00:00:00"。

        A2:在A1基础上增加一个新的字段lrr,即按月比上期和比去年同期,前者表达式为mAmount/mAmount[-1]。集算器可以用 [N][-N]来表达相对于当前记录之后的第N条记录,或之前的第N条记录,因此代码中mAmount代表当期销售额,mAmount[-1]代表上期销售额。需要注意的是,初始月份的比上期值为空(即20111月)。

 

        A3:将A2按照月、年排序,以便计算同期比。完整的代码应当是:=A3.sort(m,y),由于A3本来就是按年排序的,因此只需按月排序就可以达到目的,即A3.sort(m),这样性能也高。

        A4:在A3的基础上计算销售额的同期比,月份相同时才进行同期比计算。

 

集算脚本的计算结果可以作为报表数据源供报表使用,还可以在JAVA程序中通过JDBC的方式读取并使用,JAVA读取调用集算脚本代码如下:

           Class.forName("com.esproc.jdbc.InternalDriver");

           con= DriverManager.getConnection("jdbc:esproc:local://");

           //调用集算器脚本(类似存储过程),其中p1是集算脚本的文件名

           st =(com. esproc.jdbc.InternalCStatement)con.prepareCall("call p1()");

           st.setObject(1,"2011-01-01 00:00:00");

 

           st.setObject(2,"2014-07-08 00:00:00");

 

                 //执行脚本

           st.execute();

           //获取结果集

           ResultSet rs = st.getResultSet();

                     ……

返回值是符合JDBC标准的ResultSet对象,调用集算器脚本和访问数据库的方法完全一样,熟悉JDBC的程序员可以很快掌握。

关于集算器JDBC的部署和调用的更详细信息可参考【集算器集成应用之被JAVA调用

 

跨行计算

       同期比环比属于一类行间计算,这里来看另一类跨行计算的例子。有数据如下:

ID   store

1     33   

2     55   

3     66   

4     88   

5     12

       要求:增加两列store1和store2,第一条记录store1=store,store2=store,从第二条开始store1=store+store1[-1],store2=store1+store2[-1],其中[-1]代表上一记录。

 

        目标结果:

ID   store       store1      store2

1     33    33    33

2     55    88    121

3     66    154  275

4     88    242  517

5     12    254  771

 

        集算器脚本:



 

        A1:执行SQL取数

        A2:创建结果集,设置store1=store+store1[-1],store2=store1+store2[-1],集算器中使用[-1]表示相对位置,即上一条记录。

 

       这里也可以看看SQL的实现,比较二者的区别:

SELECT T1.ID,T1.store

    ,SUM(DISTINCT T2.store)store1

    ,SUM(T3.store)store2

FROM test T1

    JOIN test T2 ON T1.ID>=T2.ID

    JOIN test T3 ON T2.ID>=T3.ID

GROUP BY T1.ID,T1.store

ORDER BY T1.ID

 

 

 

 

 

连续升降统计

       股票记录数据如下,要求计算Price连续为正或连续为负的天数。

Date Price

2015-1-1 3

2015-1-2 4

2015-1-3 347

2015-1-4 464

2015-1-5 35

2015-1-6 363

2015-1-7 -5

2015-1-8 -3

2015-1-9 -5

2015-1-10      37

2015-1-11       896

2015-1-12      36

2015-1-13      -636

2015-1-14      -353

2015-1-15      -242

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

        目标结果:

Date Price       result

2015-01-01     3     1

2015-01-02     4     2

2015-01-03     347  3

2015-01-04     464  4

2015-01-05     35    5

2015-01-06     363  6

2015-01-07     -5    1

2015-01-08     -3    2

2015-01-09     -5    3

2015-01-10     37    1

2015-01-11     896  2

2015-01-12     36    3

2015-01-13     -636 1

2015-01-14     -353 2

2015-01-15     -242 3

 

        集算器脚本:



 

        A1:执行SQL取数并按日期排序;

        A2:增加result字段,如果当前Price值和上一条Price值同为正或同为负,则result加1,否则为1。

 

       在集算器中使用相对位置[-1]表示上一条记录,还能用(-2)取倒数第2条,用{-1,1}表示相对区间,可以简化复杂的行间计算

连续数据过滤

        有数据如下:

id    num

1     1

2     1

3     1

4     2

5     1

6     2

7     3

8     3

9     3

10    3

11    4

12    4

        要求:统计num中至少连续出现3次的num值。

 

        目标结果:

1

3

 

        集算器脚本:



 

        A1:执行SQL取数;

        A2:创建变量lx并赋初值为1;

        A3:判断当前记录的num值如果与上一记录的num相等,则lx+1,最后选出lx为3的num值。

相邻数据查询

       有如下数据:

Date Property1       Property2

2014-11-12 00:00:00     4     88

2014-11-13 00:00:00     5     33

2014-11-14 00:00:00     7     200

2014-11-15 00:00:00     15    50

2014-11-16 00:00:00     8     23

2014-11-22 00:00:00     4     142

2014-11-23 00:00:00     58    8

        要求:查询Property2大于100的记录和它的下一条记录。

 

        目标结果:

Date Property1       Property2

2014-11-14 00:00:00     7     200

2014-11-15 00:00:00     15    50

2014-11-22 00:00:00     4     142

2014-11-23 00:00:00     58    8

 

        集算器脚本:



 

        A1:执行SQL取数;

        A2:获取Property2>100记录的位置;

        A3:根据A2的位置序列,从A1中取所在位置记录和下一条记录。

区间计数

       有表数据如下:

id    value

1     11

2     3

3     12

4     2

5     1

6     3

7     15

8     24

9     5

10    15

        要求:统计value小于10(或其他值)的行数,但如果多个连续行均小于10,则连续多行只计为1行,所以上述数据的计算结果应为3.

 

 

        集算器脚本:



 

        A1:执行SQL取数;

        A2:按value大于10或小于10做相邻分组,@o表示只比较相邻数据,再统计value小于10的分组个数。

 

       不同于SQL分组后必须聚合,集算器的分组可以保留分组成员以便后续计算使用,因而可以简化SQL运算。

 

       同样可看看SQL的实现:

select count(*)

from (select t.*,

             if(@pp := @prev,

                if(@prev := value, @pp, @pp)

               ) as prev_value

      from table t cross join

           (select @prev := NULL) vars

      order by id

     ) t

where value > 10 and (prev_value is null or prev_value < 10);

 

区间过滤

       有数据如下:

ID   DDATE

1     2013-5-7

2     2013-8-6

3     2013-6-12

4     2013-12-1

        要求:根据ID升序排序,找出DDATE乱序的记录,即日期未按升序排列的记录。

 

        目标结果:

ID   DDATE

2     2013-8-6

3     2013-6-12

 

        集算器脚本:



 

        A1:执行SQL取数,按ID排序;

        A2:选出 与前面所有记录比较,不是最大的;或者与后面所有记录比较,不是最小的记录。其中DDATE{,0}表示开始到当前所有DDATE集合,DDATE{0,}表示当前到最后所有DDATE集合。

 

        SQL的实现:

WITH T1 as (

select id,ddate,max(ddate)over(order by id) d1

from t0042),T2 as (

select id,min(ddate)over(order by id desc) d2

from t0042)

select T1.id,T1.ddate

from T2,T1

WHERE T1.id=T2.id and (t1.ddate<>T1.d1 or t1.ddate<>T2.d2)

 

区间补齐

       有数据如下:

A

1001

1002

1003

1007

1008

1009

1010

1012

        要求:找出根据A字段最小和最大值生成的数值区间内,缺失的数字。

 

        目标结果:

A

1004

1005

1006

1011

 

        集算器脚本:



 

        A1:执行SQL查询数据,并按A排序;

        A2:根据首位记录生成连续区间,计算其与A1的补集得到目标结果。

 

        SQL实现:

SELECT 1000+T2.number

FROM TB T1 RIGHT JOIN master..spt_values T2 ON T1.A=T2.number+1000

WHERE T2.type='P'AND T2.number>0 AND 1000+T2.number<=(SELECT MAX(A)FROM TB) AND T1.A IS NULL

 

区间合并

       有数据如下:

xh    num1      num2

1     10015     30080

2     20022     50090

3     60250     90900

        要求:判断各条记录中num1和num2生成的区间是否重叠,若重叠则合并。

 

        目标结果:

xh    num1      num2

1     10015     50090

2     60250     90900

 

     思路是判断是否可以和上一行合并成同一个区间,如果不可以则处理成另一个分组(+1),可以则处理成同一个分组(+0),最后在每组算最小最大端点即可。

 

        集算器脚本:



 

        A1:执行SQL取数,并按num1排序;

        A2:设置临时变量a,并初始化值为0;

        A3:使用排序后的num1与上一条num2做比较,大于属于下一分组,否则合并区间生成新区间。

 

        SQL实现:

select row_number()over(order by rn) xh,

       min(num1) num1,

       max(num2) num2

  from

(

select num1,

       num2,

       last_value(case when rn is not null then rn end ignore nulls)over(order by rownum) rn

  from    

(

select num1,

       num2,

       lag(num2)over(order by xh),

       (case when num1 - lag(num2)over(order by xh) < 0 then null else xh end) rn

  from tt

) t

) t1

group by  rn

 

区间生成

有数据如下:

RBD_ID  ODFEE_ID    RBD_VALUE

25    206  ALL

31    205  A

26    205  B

41    205  C

42    205  D

30    205  E

38    205  F

39    205  H

27    205  J

35    205  K

32    205  N

36    205  O

28    205  Q

44    205  R

34    205  S

29    205  U

33    205  V

40    205  X

37    205  Y

43    205  Z

        要求:将RBD_VALUE字段值拼成分段的字符串,形如: A-F,H,J-K,N-O,Q-S,U-V,X-Z

        具体规则是:

如果RBD_VALUE=“ALL”,则舍去该值。

如果RBD_VALUE前后都是连续的字母,则取第一个字母和最后一个字母作为一段,中间用横杠,比如“A-F”。

如果RBD_VALUE出现间隔,比如K和N之间缺少L和M,则K之前作为一段,N之后作为一段,两段之间用逗号分隔,形如:“J-K,N-O”。

如果RBD_VALUE前后都不连续,比如H前面缺少G,后面缺少I,则当前值单独作为一段。

 

        目标结果:

A-F

H

J-K

N-O

Q-S

U-V

X-Z

 

        集算器脚本:



 

        A1:执行SQL取数;

        A2:将A1的RBD_VALUE按照字母表对齐;

        A3:对A2进行分组,RBD_VALUE等于null时分一组,不等于null时再分一组。@o表示归并式分组,只对前后数据进行比较。

        A4:选出非空的组。

 

        A5:根据组内成员个数拼凑字符串。~.m(-1)表示取当前组的最后一条记录。

  • 大小: 45.8 KB
  • 大小: 16.3 KB
  • 大小: 17 KB
  • 大小: 16.1 KB
  • 大小: 13 KB
  • 大小: 12.5 KB
  • 大小: 18.8 KB
  • 大小: 16 KB
  • 大小: 22.4 KB
  • 大小: 29.4 KB
2
0
分享到:
评论

相关推荐

    Notepad++ sql格式化插件 Poor Man's T-SQL Formatter 1.5.1

    格式化可能包括缩进调整、换行处理、注释排列等,使得原本杂乱的SQL代码变得整洁有序。 在使用Notepad++配合"Poor Man's T-SQL Formatter"插件进行SQL开发时,开发者可以享受以下优势: - **提高代码可读性**:格式...

    SQL速查手册中文

    这个“SQL速查手册中文”提供了丰富的实例和示例代码,可以帮助用户快速理解和应用SQL语法,解决实际问题。无论你是数据库管理员、数据分析师还是软件开发者,都能从中受益,提升SQL技能。通过CHM文件的搜索功能,你...

    asp.net core6 MVC+SqlServer例子

    ASP.NET Core 6 MVC + SQLServer 示例项目是一个现代Web应用程序开发框架的应用实例,适用于使用C#编程语言构建基于Web的应用程序。在这个项目中,我们将会深入探讨ASP.NET Core 6 MVC框架与SQL Server数据库的集成...

    达内云笔记完整项目包含sql文件

    除了`.sql`和`.jar`文件,"笔记"部分可能包含课程的讲义、学习指南、示例代码或者问题解答,这些都是学习过程中非常宝贵的参考资料。这些笔记可以帮助学习者理解课程内容,解决在编程实践中遇到的问题,从而提升学习...

    Go-Dotsql一个使用SQL的Golang库

    这个库的设计目标是帮助开发者将SQL脚本文件存储在一个集中的位置,以便于维护和管理,同时简化在代码中使用这些SQL语句的过程。 在传统的项目开发中,SQL语句通常被硬编码在Go代码中,这可能导致代码可读性和可...

    pl/sql集合相关例子

    数组是一种有序的数据结构,其中的元素通过索引进行访问。PL/SQL支持两种类型的数组:索引数组(Indexed Arrays)和变长数组(VARRAYs)。 - **索引数组**:索引数组是固定大小的数组,每个元素都有一个唯一的索引...

    sql优化

    - 如果字段中存在NULL值,则任何涉及该字段的计算结果都会是NULL。 - 在某些情况下,即使字段本身不是NULL,但只要涉及到NULL的操作(如加法),其结果也会被标记为NULL。 - **实践应用**: - 避免使用`column ...

    PL/SQL 语法手册

    7. **应用实例**:提供了实际的代码示例,帮助读者理解并应用PL/SQL语法。 通过这份手册,读者不仅可以学习到SQL的基础操作,还能深入理解PL/SQL的高级特性,从而更高效地进行数据库开发和管理。手册中的每一个章节...

    AJAX+LINQ+SQL开发迷你博客

    描述中提到的"源码项目:AajxMiniBlog",意味着我们有一个实际的代码示例可供参考,而数据库"App_Data/MiniBlog.MDF"则表明博客的数据存储在本地的SQL Server Compact Edition数据库文件中。 **AJAX详解** AJAX是...

    redis demo 示例

    - MyBatis是一个优秀的持久层框架,它简化了Java与数据库之间的交互,通过XML或注解方式配置SQL和映射,实现了SQL语句的动态生成。 - MyBatis的工作原理:Mapper接口定义了数据库操作,XML或注解中的SQL语句与接口...

    sql数据库基础面试题复习题考试题-全.docx

    - **功能描述**:SQL Server 2008 是一个功能强大的数据库和数据分析平台 (选项 B、C、D),支持大规模在线事务处理、数据仓库和电子商务应用,并提供友好的用户界面工具,简化了从移动设备到企业数据库系统的多平台...

    分页存储过程 oracle sql

    这种方式不仅能够加快页面加载速度,还能够使数据展示更加清晰有序。 ### Oracle SQL中的分页实现 在Oracle SQL中实现分页主要有两种方式:使用`ROWNUM`变量和子查询;或者使用`OFFSET`和`FETCH`关键字(仅在...

    2021-2022计算机二级等级考试试题及答案No.17175.docx

    - **示例分析**:题目中的完全二叉树共有699个节点,要确定叶子节点的数量,首先计算出树的高度,然后根据完全二叉树的性质推断出叶子节点的数量。在这种情况下,该完全二叉树的高度为10(因为`2^9 ^10`),根据...

    struts-2.3、spring2.5.6、hibernate3.3整合示例

    **Struts 2** 是一个基于MVC(Model-View-Controller)设计模式的Web应用框架,它极大地简化了Java Servlet API的使用,提供了一种更有序的方式来组织控制逻辑。Struts 2的核心在于Action类,它处理用户请求并调用...

    SQL SERVER 2012新增函数之逻辑函数CHOOSE详解

    这个函数的主要作用是从一个有序的值列表中,根据提供的索引返回对应的元素。这使得在处理多值选择或映射时变得更加便捷。 CHOOSE函数的基本语法如下: ```sql CHOOSE(index, value1, value2, ..., valueN) ``` 在...

    _html_css_javascript_sql_jdbc_jsp(el_jstl)_servlet

    在给出的示例中,可以看到HTML的基本标签,如`&lt;html&gt;`、`&lt;head&gt;`、`&lt;body&gt;`,以及用于文本输入的`&lt;form&gt;`、文本框`&lt;input type="text"&gt;`、密码框`&lt;input type="password"&gt;`、单选按钮`&lt;input type="radio"&gt;`、多选...

    基于python实现的sqlite队列sqlite-queue-python-master

    “并且包含一个十分简洁好用的SQL语句包装”,表明该库还提供了一种方式来简化SQL语句的编写,可能是通过一个SQL查询构建器或者ORM(对象关系映射)系统。这样的工具可以提高代码的可读性和可维护性,同时也降低了...

    2021-2022计算机二级等级考试试题及答案No.40.docx

    - **知识点概述**:Java 的垃圾回收机制自动管理内存,防止内存泄漏,简化了内存管理过程。 - **优点**:自动回收不再使用的对象所占用的内存空间,提高了内存利用率。 - **原理**:Java 虚拟机定期检查堆内存中不再...

    SQL向导第八部分-理货表

    总之,理货表是SQL中的一个强大工具,能够提高查询性能,简化复杂操作。通过深入理解和巧妙运用,开发者可以在处理大量数据时更加得心应手。在进行数据库设计和优化时,理货表绝对值得考虑和掌握。

Global site tag (gtag.js) - Google Analytics