SQL经典实例——报表制作和整形

Intouch与SQL Server及Excel报表系统联调操作指南 工业自动化项目中,SCADA系统与关系型数据库的联动是数据追溯与报表生成的关键环节。通过标准SQL协议,上位机组态软件可将实时采集的生产数据写入数据库,再由报表工具按需提取。本文围绕WonderWare Intouch 2014R2与SQL Server 2012的集成实践,梳理了混合身份验证配置、绑定列表映射、SQLConnect连接字符串、周期写入脚本以及Excel通过VBA调用ADO查询数据库的完整链路。同时总结了SQLInsert无报错但不落库、连接返回-1、跨天防重等典型排障经验,并给出了三层验证 阅读详情

1、将结果集转置为一行

问题:你想将多行中的值转换为单行中的列。例如,有一个结果集,它显示了每个部门的员工数量。

DEPTNO        CNT
------ ----------
    10          3
    20          5
    30          6

而你要调整输出的格式,让这个结果集展示如下结果。

DEPTNO_10   DEPTNO_20   DEPTNO_30
---------  ----------  ----------
        3           5           6

这是一个经典示例,它以不同于存储形状的方式呈现数据。

解决方案:使用 CASE 和聚合函数 SUM 来转置这个结果集。

select sum(case when deptno=10 then 1 else 0 end) as deptno_10,
       sum(case when deptno=20 then 1 else 0 end) as deptno_20,
       sum(case when deptno=30 then 1 else 0 end) as deptno_30
from emp;

 deptno_10 | deptno_20 | deptno_30 
-----------+-----------+-----------
         3 |         5 |         6
(1 row)

2、将结果集转置为多行

问题:通过为给定列中每个不同的值都创建一列,你想将行转换为列,但与上一节的实例不同,你想输出多行。与转置为一行一样,转置为多行也是一种基本的数据整形方法。

例如,你想返回每位员工及其职位(JOB)​,但当前使用的查询返回的结果集如下所示。

JOB        ENAME
---------  ----------
ANALYST   SCOTT
ANALYST   FORD
CLERK     SMITH
CLERK     ADAMS
CLERK     MILLER
CLERK     JAMES
MANAGER   JONES
MANAGER   CLARK
MANAGER   BLAKE
PRESIDENT KING
SALESMAN  ALLEN
SALESMAN  MARTIN
SALESMAN  TURNER
SALESMAN  WARD

你想调整结果集的格式,让每种职位各为一列。

CLERKS  ANALYSTS  MGRS   PREZ  SALES
------  --------  -----  ----  ------
MILLER  FORD      CLARK  KING  TURNER
JAMES   SCOTT     BLAKE        MARTIN
ADAMS             JONES        WARD
SMITH                          ALLEN

不同于上一节的实例,本实例的结果集包含多行。上一节介绍的方法对本实例不管用,因为按 JOB 分组并使用MAX(ENAME) 时,将为每个 JOB 都返回一个 ENAME,即像上一节的实例一样,将只返回一行。要解决这个问题,必须让每个 JOB/ENAME 组合都是独一无二的。这样使用聚合函数来删除 NULL 时,就不会丢失任何 ENAME了。

使用排名函数 ROW_NUMBER OVER 让每个 JOB/ENAME组合都是独一无二的。使用 CASE 表达式和聚合函数MAX 对结果集进行转置,并按窗口函数 ROW_NUMBEROVER 返回的值分组。

select max(case when job='CLERK'
							 then ename else null end) as clerks,
			max(case when job='ANALYST'
							 then ename else null end) as analysts,
			max(case when job='MANAGER'
							 then ename else null end) as mgrs,
			max(case when job='PRESIDENT'
							 then ename else null end) as prez,
			max(case when job='SALESMAN'
							 then ename else null end) as sales
from (
	select job,
			 ename,
			 row_number()over(partition by job order by ename) rn
	from emp
) x
group by rn;


 clerks | analysts | mgrs  | prez | sales  
--------+----------+-------+------+--------
 MILLER |          | JONES |      | TURNER
 SMITH  |          |       |      | WARD
 JAMES  | SCOTT    | CLARK |      | MARTIN
 ADAMS  | FORD     | BLAKE | KING | ALLEN
(4 rows)

首先,借助窗口函数 ROW_NUMBER OVER 让每一个JOB/ENAME 组合都是独一无二的。

select job,
       ename,
       row_number()over(partition by job order by ename) rn
  from emp

JOB       ENAME              RN
--------- ---------- ----------
ANALYST   FORD                1
ANALYST   SCOTT               2
CLERK     ADAMS               1
CLERK     JAMES               2
CLERK     MILLER              3
CLERK     SMITH               4
MANAGER   BLAKE               1
MANAGER   CLARK               2
MANAGER   JONES               3
PRESIDENT KING                1
SALESMAN  ALLEN               1
SALESMAN  MARTIN              2
SALESMAN  TURNER              3
SALESMAN  WARD                4

通过给每一个 ENAME 指定一个在当前职位中独一无二的“行号”​,可以防范因两位员工具有相同名字和职位而带来的问题。这里的目标是,确保在按行号(RN)分组的情况下,使用 MAX 不会导致结果集中的任何员工丢失。解决这个问题时,这是最重要的步骤。如果没有这一步,外部查询中的聚合就会删除原本要返回的行。

3、对结果集进行逆转置

问题:你想将列转换为行。请看下面的结果集:

DEPTNO_10  DEPTNO_20  DEPTNO_30
---------- ---------- ----------
         3          5          6

你想将它转换成下面这样。

DEPTNO COUNTS_BY_DEPT
------ --------------
    10              3
    20              5
    30              6

你可能注意到了,上面的第一个结果集就是 12.1 节实例的输出。为了在本实例中使用这个结果集,可以使用如下查询将其存储在视图中。

create view emp_cnts as (
	select sum(case when deptno=10 then 1 else 0 end) as deptno_10,
			sum(case when deptno=20 then 1 else 0 end) as deptno_20,
			sum(case when deptno=30 then 1 else 0 end) as deptno_30
	from emp
);

在接下来的解决方案和讨论中,将引用这个查询创建的视图 EMP_CNTS。

解决方案:从要获得的结果集很容易看出,可以对 EMP 表使用COUNT 和 GROUP BY 来生成它。但这里假设数据并不是以行的方式存储的:数据可能是非规范化的,而聚合值存储在多列中。

要将列转换为行,可以使用笛卡儿积。我们需要事先知道要转换为行的列数,因为用来生成笛卡儿积的表表达式的基数不能小于要转置的列数。

本解决方案不会创建非规范化数据表,而会使用 11 节实例中的解决方案来创建一个“宽”结果集。完整的解决方案如下所示。

select dept.deptno,
	case dept.deptno
		 when 10 then emp_cnts.deptno_10
		 when 20 then emp_cnts.deptno_20
		 when 30 then emp_cnts.deptno_30
	end as counts_by_dept
from emp_cnts cross join (
	select deptno 
	from dept 
	where deptno <= 30) dept;

4、将结果集逆转置为一列

问题:你想将查询返回的所有列值都放在一列中,并返回它们。例如,你想返回 10 号部门所有员工的 ENAME、JOB 和SAL,并想将这 3 个值放在一列中。为此,你要为每位员工返回 3 行,并在员工之间添加一个空白行。你想返回的结果集如下所示。

EMPS
----------
CLARK
MANAGER
2450

KING
PRESIDENT
5000

MILLER
CLERK
1300

解决方案:关键是结合使用递归 CTE 和笛卡儿积来为每位员工返回4 行数据。使用笛卡儿连接,可以在每一行中返回一个列值,并在员工之间添加空白行。

使用窗口函数 ROW_NUMBER OVER 根据 EMPNO 给行排名(1~4)​,然后使用 CASE 表达式将 3 列转换为 1 列(在 PostgreSQL 和 MySQL 中,必须在 WITH 后面添加关键字 RECURSIVE)​。

  with four_rows (id)
    as
  (
   select 1
     union all
   select id+1
     from four_rows
     where id < 4
   )
   ,
    x_tab (ename,job,sal,rn )
     as
   (
       ect e.ename,e.job,e.sal,
      row_number()over(partition by e.empno
      order by e.empno)
      from emp e
      join four_rows on 1=1
    )

   select
     case rn
     when 1 then ename
     when 2 then job
     when 3 then cast(sal as char(4))
    end emps
  from x_tab;

5、消除结果集中的重复值

问题:你正在制作一张报表,当多行的同一列的值相同时,你希望这个列值只显示一次。例如,你想从 EMP 表返回DEPTNO 和 ENAME 并按 DEPTNO 分组,但对于每个DEPTNO,只想显示一次。你想返回如下结果集。

DEPTNO ENAME
------ ---------
    10 CLARK
       KING
       MILLER
    20 SMITH
       ADAMS
       FORD
       SCOTT
       JONES
    30 ALLEN
       BLAKE
       MARTIN
       JAMES
       TURNER
       WARD

解决方案:这是一个简单的格式设置问题,使用窗口函数 LAG OVER可以轻而易举地解决。

select
	case when lag(deptno)over(order by deptno) = deptno then null 
	else deptno 
	end DEPTNO, ename
from emp;

 deptno | ename  
--------+--------
     10 | MILLER
        | CLARK
        | KING
     20 | SCOTT
        | JONES
        | SMITH
        | ADAMS
        | FORD
     30 | WARD
        | TURNER
        | ALLEN
        | BLAKE
        | MARTIN
        | JAMES
(14 rows)

Oracle 用户也可以用 DECODE 替代 CASE。

select to_number(
        decode(lag(deptno)over(order by deptno),
            deptno,null,deptno)
    ) deptno, ename
from emp;

6、转置结果集以简化涉及多行的计算

问题:你要执行的计算涉及来自多行的数据,为简化工作,你想将这些行转置为列,这样你需要的所有数据都会出现在同一行中。

薪水总额最高的部门是 20 号部门。要确认这一点,可以执行下面的查询。

select deptno, sum(sal) as sal
from emp
group by deptno

DEPTNO        SAL
------ ----------
    10       8750
    20      10875
    30       9400

你想计算 10 号部门的薪水总额和 30 号部门的薪水总额分别比 20 号部门的薪水总额少多少,最终结果如下所示。

d20_10_diff    d20_30_diff
------------   ----------
2125            1475

解决方案:使用聚合函数 SUM 和 CASE 表达式对部门薪水总额进行转置,然后在 SELECT 列表中编写计算薪水总额之差的表达式。

select d20_sal - d10_sal as d20_10_diff,
    d20_sal - d30_sal as d20_30_diff
from (
	select sum(case when deptno=10 then sal end) as d10_sal,
			sum(case when deptno=20 then sal end) as d20_sal,
			sum(case when deptno=30 then sal end) as d30_sal
	from emp
) totals_by_dept;

 d20_10_diff | d20_30_diff 
-------------+-------------
        2125 |        1475
(1 row)

也可以使用 CTE 来编写这个查询,有些人可能觉得这样做可读性更高。

with totals_by_dept (d10_sal, d20_sal, d30_sal) as (
	select
		sum(case when deptno=10 then sal end) as d10_sal,
		sum(case when deptno=20 then sal end) as d20_sal,
		sum(case when deptno=30 then sal end) as d30_sal
	from emp
)
select d20_sal - d10_sal as d20_10_diff,
			 d20_sal - d30_sal as d20_30_diff
from totals_by_dept;

7、创建尺寸固定的数据桶

问题:你想将数据划分到大小相同的桶中,其中每个桶包含的元素个数是事先定好的。总桶数可能未知,但你要确保每个桶中都包含 5 个元素。例如,你想基于 EMPNO 值将 EMP表中的员工分组,每组包含 5 位员工,如下面的结果所示。

GRP      EMPNO ENAME
--- ---------- -------
  1       7369 SMITH
  1       7499 ALLEN
  1       7521 WARD
  1       7566 JONES
  1       7654 MARTIN
  2       7698 BLAKE
  2       7782 CLARK
  2       7788 SCOTT
  2       7839 KING
  2       7844 TURNER
  3       7876 ADAMS
  3       7900 JAMES
  3       7902 FORD
  3       7934 MILLER

解决方案:使用排名函数可以极大地简化这个问题的解决方案。将行排名后,要创建大小为 5 的桶,只需执行除法运算并将商向上取整。

使用窗口函数根据 EMPNO 对员工进行排名,然后除以 5以创建分组。​(如果你使用的是 SQL Server,请将CEIL 替换为 CEILING。​)

select ceil(row_number()over(order by empno)/5.0) grp,
    empno,
    ename
from emp;

 grp | empno | ename  
-----+-------+--------
   1 |  7369 | SMITH
   1 |  7499 | ALLEN
   1 |  7521 | WARD
   1 |  7566 | JONES
   1 |  7654 | MARTIN
   2 |  7698 | BLAKE
   2 |  7782 | CLARK
   2 |  7788 | SCOTT
   2 |  7839 | KING
   2 |  7844 | TURNER
   3 |  7876 | ADAMS
   3 |  7900 | JAMES
   3 |  7902 | FORD
   3 |  7934 | MILLER
(14 rows)

8、创建预定数量的桶

问题:你想将数据划分到数量固定的几个桶中。例如,你想将EMP 表中的员工划分到 4 个桶中,结果如下所示。

GRP EMPNO ENAME
--- ----- ---------
  1  7369 SMITH
  1  7499 ALLEN
  1  7521 WARD
  1  7566 JONES
  2  7654 MARTIN
  2  7698 BLAKE
  2  7782 CLARK
  2  7788 SCOTT
  3  7839 KING
  3  7844 TURNER
  3  7876 ADAMS
  4  7900 JAMES
  4  7902 FORD
  4  7934 MILLER

这是一种组织分类数据的常见方式,因为在很多分析中,将一个集合分成多个规模相同的集合是重要的第一步。例如,通过计算这些分组的平均薪水或其他平均值,或许能够揭示查看各个值时被波动掩盖的趋势。

这个问题与 7 节的问题相反。在 7 节中,桶的数量是未知的,但每个桶包含的元素个数是事先定好的,而在这个问题中,可能不知道每个桶中有多少个元素,但要创建的桶的个数是固定(已知)的。

解决方案:由于现在很多 RDBMS 支持函数 NTILE,因此这个问题的解决方案很简单。NTILE 会将一个有序集合划分到指定数量的桶中。如果集合元素无法均分,就将多出来的元素放到前面的桶中,本实例要返回的结果集反映了这一点:第 1 个桶和第 2 个桶分别有 4 行数据,而第 3 个桶和第 4 个桶分别只有 3 行数据。

使用窗口函数 NTILE 创建 4 个桶。

select 
	ntile(4)over(order by empno) grp,
	empno,
	ename
from emp;

 grp | empno | ename  
-----+-------+--------
   1 |  7369 | SMITH
   1 |  7499 | ALLEN
   1 |  7521 | WARD
   1 |  7566 | JONES
   2 |  7654 | MARTIN
   2 |  7698 | BLAKE
   2 |  7782 | CLARK
   2 |  7788 | SCOTT
   3 |  7839 | KING
   3 |  7844 | TURNER
   3 |  7876 | ADAMS
   4 |  7900 | JAMES
   4 |  7902 | FORD
   4 |  7934 | MILLER
(14 rows)

9、创建水平直方图

问题:你想使用 SQL 来创建沿水平方向延伸的直方图。例如,你想以水平直方图的方式显示每个部门的员工数量,在直方图中每个星号(*)表示一位员工。你想返回的结果集如下所示。

DEPTNO CNT
------ ----------
    10 ***
    20 *****
    30 ******

解决方案:本解决方案的关键是,使用聚合函数 COUNT 和 GROUPBY DEPTNO 来计算每个部门的员工数量。然后,将COUNT 返回的值传递给一个字符串函数,以生成相应数量的 * 字符。

DB2:使用函数 REPEAT 生成直方图。

select deptno,
repeat('*',count(*)) cnt
from emp
group by deptno;

Oracle、PostgreSQL 和 MySQL:使用函数 LPAD 生成相应数量的 * 字符。

select deptno,lpad('*',count(*),'*') as cnt
from emp
group by deptno;

在这里插入图片描述
SQL Server:使用函数 REPLICATE 生成直方图。

select deptno,replicate('*',count(*)) cnt
from emp
group by deptno;

10、创建垂直直方图

问题:你想生成一个从下往上延伸的直方图。例如,你想以垂直直方图的方式显示每个部门的员工数量,每位员工用一个* 字符表示。你想返回的结果集如下所示。

D10 D20 D30
--- --- ---
        *
    *   *
    *   *
*   *   *
*   *   *
*   *   *

解决方案:这个问题的解决方案以本章前面使用的一种方案为基础:使用函数 ROW_NUMBER OVER 来唯一地表示每个部门的每位员工。使用聚合函数 MAX 来转置结果集,并根据函数 ROW_NUMBER OVER 返回的值进行分组。​(如果你使用的是 SQL Server,请不要在 ORDER BY 子句中使用DESC。​)

select max(deptno_10) d10,
		 max(deptno_20) d20,
		 max(deptno_30) d30
from (
	select row_number()over(partition by deptno order by empno) rn,
			 case when deptno=10 then '*' else null end deptno_10,
			 case when deptno=20 then '*' else null end deptno_20,
			 case when deptno=30 then '*' else null end deptno_30
	from emp
) x
group by rn
order by 1 desc, 2 desc, 3 desc;

在这里插入图片描述

11、返回未被用作分组依据的列

问题:你正在编写一个 GROUP BY 查询,并要返回未包含在GROUP BY 子句中的列。这通常是不可能的,因为未被用作分组依据的列在各行中不是唯一的。

假设你想找出每个部门中薪水最高和薪水最低的员工,以及每个职位中薪水最高和薪水最低的员工。你要显示每位员工的名字、所属部门、职位和薪水。你想返回的结果集如下所示。

DEPTNO ENAME  JOB         SAL DEPT_STATUS     JOB_STATUS
------ ------ --------- ----- --------------- --------------
    10 MILLER CLERK      1300 LOW SAL IN DEPT TOP SAL IN JOB
    10 CLARK  MANAGER    2450                 LOW SAL IN JOB
    10 KING   PRESIDENT  5000 TOP SAL IN DEPT TOP SAL IN JOB
    20 SCOTT  ANALYST    3000 TOP SAL IN DEPT TOP SAL IN JOB
    20 FORD   ANALYST    3000 TOP SAL IN DEPT TOP SAL IN JOB
    20 SMITH  CLERK       800 LOW SAL IN DEPT LOW SAL IN JOB
    20 JONES  MANAGER    2975                 TOP SAL IN JOB
    30 JAMES  CLERK       950 LOW SAL IN DEPT
    30 MARTIN SALESMAN   1250                 LOW SAL IN JOB
    30 WARD   SALESMAN   1250                 LOW SAL IN JOB
    30 ALLEN  SALESMAN   1600                 TOP SAL IN JOB
    30 BLAKE  MANAGER    2850 TOP SAL IN DEPT

可惜在 SELECT 子句中包含所有这些列将影响分组。来看一个例子。员工 KING 的薪水最高,你想使用下面的查询来验证这一点。

select ename,max(sal)
  from empgroup by ename

这个查询将返回 EMP 表中的全部 14 行数据,而不是员工KING 及其薪水。问题出在分组上:MAX(SAL) 被应用于每个 ENAME。上述查询的意思好像是找出薪水最高的员工,但它实际上做的是找出 EMP 表中每位员工的最高薪水。本实例将介绍如何在 GROUP BY 子句中不包含ENAME 列的情况下返回它。

解决方案:使用内嵌视图找出相应部门和职位的最高薪水和最低薪水,然后只保留获得这些薪水的员工。

使用窗口函数 MAX OVER 和 MIN OVER 找出相应部门和职位的最高薪水和最低薪水,然后保留薪水为相应部门或职位的最高薪水或最低薪水的行。

select deptno,ename,job,sal,
			case when sal = max_by_dept
					 then 'TOP SAL IN DEPT'
					 when sal = min_by_dept
					 then 'LOW SAL IN DEPT'
			end dept_status,
			case when sal = max_by_job
					 then 'TOP SAL IN JOB'
					 when sal = min_by_job
					 then 'LOW SAL IN JOB'
			end job_status
 from (
select deptno,ename,job,sal,
			max(sal)over(partition by deptno) max_by_dept,
			max(sal)over(partition by job) max_by_job,
			min(sal)over(partition by deptno) min_by_dept,
			min(sal)over(partition by job) min_by_job
 from emp
			) emp_sals
where sal in (max_by_dept,max_by_job,
							min_by_dept,min_by_job);

在这里插入图片描述

12、计算简单的小计

问题:在本实例中,你想创建一个结果集,其中包含小计(聚合分组的特定列)和总计(聚合整张表的特定列)​。一个这样的结果集既包含 EMP 表中每种职位的薪水总额,也包含 EMP 表中所有薪水的总额,其中每种职位的薪水总额为小计,所有薪水的总额为总计。这种结果集如下所示。

JOB              SAL
--------- ----------
ANALYST         6000
CLERK           4150
MANAGER         8275
PRESIDENT       5000
SALESMAN        5600
TOTAL          29025

解决方案:GROUP BY 子句的 ROLLUP 扩展完美地解决了这个问题。如果你使用的 RDBMS 不支持 ROLLUP,则可以使用标量子查询或 UNION 查询来解决这个问题,只是难度更大。

DB2 和 Oracle:使用聚合函数 SUM 计算薪水总额,并使用 GROUP BY 的ROLLUP 扩展将结果组织为小计(针对不同职位)和总计(针对整张表)​。

select case grouping(job)
            when 0 then job
            else 'TOTAL'
       end job,
       sum(sal) sal
from emp
group by rollup(job);

SQL Server 和 MySQL:首先,使用聚合函数 SUM 计算薪水总额,并使用 WITHROLLUP 将结果组织为小计(针对不同职位)和总计(针对整张表)​。然后,使用 COALESCE 给总计行提供标签TOTAL。​(如果不这样做,该行的 JOB 列将为NULL。​)

select coalesce(job,'TOTAL') job,sum(sal) sal
from emp
group by job with rollup;

在这里插入图片描述
在 SQL Server 中,也可以像 Oracle/DB2 解决方案那样使用函数 GROUPING(而不是函数 COALESCE)来确定 聚合等级。

PostgreSQL:与 SQL Server 和 MySQL 解决方案类似,也可以使用GROUP BY 的 ROLLUP 扩展,但语法稍有不同。

select coalesce(job,'TOTAL') job, sum(sal) sal
from emp
group by rollup(job);

    job    |  sal  
-----------+-------
 TOTAL     | 29025
 CLERK     |  4150
 PRESIDENT |  5000
 MANAGER   |  8275
 SALESMAN  |  5600
 ANALYST   |  6000
(6 rows)

13、计算各种可能的小计

问题:你想找出不同部门、职位、职位 / 部门组合的薪水小计,同时显示整个 EMP 表的薪水总计。换言之,你想返回如下结果集。

DEPTNO JOB       CATEGORY                  SAL
------ --------- --------------------- -------
    10 CLERK     TOTAL BY DEPT AND JOB    1300
    10 MANAGER   TOTAL BY DEPT AND JOB    2450
    10 PRESIDENT TOTAL BY DEPT AND JOB    5000
    20 CLERK     TOTAL BY DEPT AND JOB    1900
    30 CLERK     TOTAL BY DEPT AND JOB     950
    30 SALESMAN  TOTAL BY DEPT AND JOB    5600
    30 MANAGER   TOTAL BY DEPT AND JOB    2850
    20 MANAGER   TOTAL BY DEPT AND JOB    2975
    20 ANALYST   TOTAL BY DEPT AND JOB    6000
       CLERK     TOTAL BY JOB             4150
       ANALYST   TOTAL BY JOB             6000
       MANAGER   TOTAL BY JOB             8275
       PRESIDENT TOTAL BY JOB             5000
       SALESMAN  TOTAL BY JOB             5600
    10           TOTAL BY DEPT            8750
    30           TOTAL BY DEPT            9400
    20           TOTAL BY DEPT           10875
                 GRAND TOTAL FOR TABLE   29025

解决方案:最近几年,对于 GROUP BY 语法的扩展使得以上问题解决起来易如反掌。如果你使用的 RDBMS 没有提供计算各种小计的扩展,就必须手动计算它们,为此可以使用自连接或标量子查询。

DB2:在 DB2 中,需要使用 CAST 将 GROUPING 返回的结果转换为数据类型 CHAR(1)。

 select deptno,
        job,
        case cast(grouping(deptno) as char(1))||
             cast(grouping(job) as char(1))
             when '00' then 'TOTAL BY DEPT AND JOB'
             when '10' then 'TOTAL BY JOB'
             when '01' then 'TOTAL BY DEPT'
             when '11' then 'TOTAL FOR TABLE'
        end category,
        sum(sal)
   from emp
  group by cube(deptno,job)
  order by grouping(job),grouping(deptno);

Oracle:结合使用 GROUP BY 子句的 CUBE 扩展和拼接运算符||。

 select deptno,
        job,
        case grouping(deptno)||grouping(job)
             when '00' then 'TOTAL BY DEPT AND JOB'
             when '10' then 'TOTAL BY JOB'
             when '01' then 'TOTAL BY DEPT'
             when '11' then 'GRAND TOTALFOR TABLE'
        end category,
        sum(sal) sal
   from emp
  group by cube(deptno,job)
  order by grouping(job),grouping(deptno);

SQL Server:使用 GROUP BY 子句的 CUBE 扩展。在 SQL Server中,需要使用 CAST 将 GROUPING 返回的结果转换为数据类型 CHAR(1),并使用拼接运算符 +(而不像在 Oracle 中那样使用运算符 ||)​。

 select deptno,
        job,
        case cast(grouping(deptno)as char(1))+
             cast(grouping(job)as char(1))
             when '00' then 'TOTAL BY DEPT AND JOB'
             when '10' then 'TOTAL BY JOB'
             when '01' then 'TOTAL BY DEPT'
             when '11' then 'GRAND TOTAL FOR TABLE'
        end category,
        sum(sal) sal
   from emp
  group by deptno,job with cube
  order by grouping(job),grouping(deptno);

PostgreSQL:PostgreSQL 解决方案与 SQL Server 解决方案类似,但CUBE 扩展和拼接运算符的语法稍有不同。

select deptno,job ,
	case concat(
		cast (grouping(deptno) as char(1)),
		cast (grouping(job) as char(1)))
	when '00' then 'TOTAL BY DEPT AND JOB'
					 when '10' then 'TOTAL BY JOB'
					 when '01' then 'TOTAL BY DEPT'
					 when '11' then 'GRAND TOTAL FOR TABLE'
	end category, 
	sum(sal) as sal
from emp
group by cube(deptno,job);

 deptno |    job    |       category        |  sal  
--------+-----------+-----------------------+-------
        |           | GRAND TOTAL FOR TABLE | 29025
     20 | CLERK     | TOTAL BY DEPT AND JOB |  1900
     30 | MANAGER   | TOTAL BY DEPT AND JOB |  2850
     10 | MANAGER   | TOTAL BY DEPT AND JOB |  2450
     10 | PRESIDENT | TOTAL BY DEPT AND JOB |  5000
     20 | ANALYST   | TOTAL BY DEPT AND JOB |  6000
     30 | SALESMAN  | TOTAL BY DEPT AND JOB |  5600
     10 | CLERK     | TOTAL BY DEPT AND JOB |  1300
     20 | MANAGER   | TOTAL BY DEPT AND JOB |  2975
     30 | CLERK     | TOTAL BY DEPT AND JOB |   950
     30 |           | TOTAL BY DEPT         |  9400
     10 |           | TOTAL BY DEPT         |  8750
     20 |           | TOTAL BY DEPT         | 10875
        | CLERK     | TOTAL BY JOB          |  4150
        | PRESIDENT | TOTAL BY JOB          |  5000
        | MANAGER   | TOTAL BY JOB          |  8275
        | SALESMAN  | TOTAL BY JOB          |  5600
        | ANALYST   | TOTAL BY JOB          |  6000
(18 rows)

MySQL:MySQL 只提供了前述解决方案使用的部分功能,准确地说是没有提供函数 CUBE。因此,在 MySQL 中,需要使用多个 UNION ALL 来生成各种可能的小计。

select deptno, job,
			'TOTAL BY DEPT AND JOB' as category,
			sum(sal) as sal
from emp
group by deptno, job
union all
select null, job, 'TOTAL BY JOB', sum(sal)
from emp
group by job
union all
select deptno, null, 'TOTAL BY DEPT', sum(sal)
from emp
group by deptno
union all
select null,null,'GRAND TOTAL FOR TABLE', sum(sal)
from emp;

在这里插入图片描述

14、标出非小计行

问题:在使用 GROUP BY 子句的 CUBE 扩展生成的报表中,你想将 GROUP BY 子句生成的行同 CUBE 或 ROLLUP 生成的行区分开。

下面的结果集是使用 GROUP BY 子句的 CUBE 扩展生成的,其显示了 EMP 表中的薪水分布情况。

DEPTNO JOB           SAL
------ --------- -------
                   29025
       CLERK        4150
       ANALYST      6000
       MANAGER      8275
       SALESMAN     5600
       PRESIDENT    5000
    10              8750
    10 CLERK        1300
    10 MANAGER      2450
    10 PRESIDENT    5000
    20             10875
    20 CLERK        1900
    20 ANALYST      6000
    20 MANAGER      2975
    30              9400
    30 CLERK         950
    30 MANAGER      2850
    30 SALESMAN     5600

这张报表包含不同 DEPTNO 和 JOB 组合的薪水小计,不同 DEPTNO 的薪水小计,不同 JOB 的薪水小计以及整个EMP 表的薪水总计,而你想清晰地指出不同的聚合等级。换言之,你想标出各个聚合值所属的类别,即 SAL 列值表示的是不同 DEPTNO 的小计,不同 JOB 的小计,还是总计?你希望返回的结果集如下所示。

DEPTNO JOB           SAL DEPTNO_SUBTOTALS JOB_SUBTOTALS
------ --------- ------- ---------------- -------------
                   29025                1             1
       CLERK        4150                1             0
       ANALYST      6000                1             0
       MANAGER      8275                1             0
       SALESMAN     5600                1             0
       PRESIDENT    5000                1             0
    10              8750                0             1
    10 CLERK        1300                0             0
    10 MANAGER      2450                0             0
    10 PRESIDENT    5000                0             0
    20             10875                0             1
    20 CLERK        1900                0             0
    20 ANALYST      6000                0             0
    20 MANAGER      2975                0             0
    30              9400                0             1
    30 CLERK         950                0             0
    30 MANAGER      2850                0             0
    30 SALESMAN     5600                0             0

解决方案:使用函数 GROUPING 来指出哪些值是 CUBE 或 ROLLUP生成的小计(超级聚合值)​。下面的解决方案适用于PostgreSQL、DB2 和 Oracle。

select deptno, jo) sal,
       grouping(deptno) deptno_subtotals,
       grouping(job) job_subtotals
from emp
group by cube(deptno,job);

与 DB2 和 Oracle 解决方案相比,SQL Server 解决方案的唯一不同之处是 CUBE/ROLLUP 子句的书写方式。

select deptno, job, sum(sal) sal,
       grouping(deptno) deptno_subtotals,
       grouping(job) job_subtotals
  from emp
 group by deptno,job with cube;

15、使用CASE表达式来标识行

问题:你想将特定列(比如 EMP 表中的 JOB 列)的值转换为“布尔”标志。例如,你想返回如下结果集。

ENAME  IS_CLERK IS_SALES IS_MGR IS_ANALYST IS_PREZ
------ -------- -------- ------ ---------- -------
KING          0        0      0          0       1
SCOTT         0        0      0          1       0
FORD          0        0      0          1       0
JONES         0        0      1          0       0
BLAKE         0        0      1          0       0
CLARK         0        0      1          0       0
ALLEN         0        1      0          0       0
WARD          0        1      0          0       0
MARTIN        0        1      0          0       0
TURNER        0        1      0          0       0
SMITH         1        0      0          0       0
MILLER        1        0      0          0       0
ADAMS         1        0      0          0       0
JAMES         1        0      0          0       0

这样的结果集很有用,不仅可以帮助调试,还可以提供不同于典型结果集的数据视图。

解决方案:使用 CASE 表达式来评估每位员工的 JOB,并返回 1 或0,以指出员工的职位。对于每种可能的职位,都需要编写一个 CASE 表达式,以生成相应的列值。

 select ename,
        case when job = 'CLERK'
             then 1 else 0
        end as is_clerk,
        case when job = 'SALESMAN'
             then 1 else 0
        end as is_sales,
        case when job = 'MANAGER'
             then 1 else 0
        end as is_mgr,
        case when job = 'ANALYST'
             then 1 else 0
        end as is_analyst,
        case when job = 'PRESIDENT'
             then 1 else 0
        end as is_prez
   from emp
  order by 2,3,4,5,6;

在这里插入图片描述

16、创建稀疏矩阵

问题:你想创建一个稀疏矩阵,比如下面这个对 EMP 表中DEPTNO 列和 JOB 列进行转置而得到的稀疏矩阵。

D10        D20        D30        CLERKS MGRS  PREZ ANALS SALES
---------- ---------- ---------- ------ ----- ---- ----- ------
           SMITH                 SMITH
                      ALLEN                              ALLEN
                      WARD                               WARD
           JONES                        JONES
                      MARTIN                             MARTIN
                      BLAKE             BLAKE
CLARK                                   CLARK
           SCOTT                                   SCOTT
KING                                          KING
                      TURNER                             TURNER
           ADAMS                 ADAMS
                      JAMES      JAMES
           FORD                                    FORD
MILLER                           MILLER

解决方案:使用 CASE 表达式来创建稀疏的行列转换结果。

select case deptno when 10 then ename end as d10,
       case deptno when 20 then ename end as d20,
       case deptno when 30 then ename end as d30,
       case job when 'CLERK' then ename end as clerks,
       case job when 'MANAGER' then ename end as mgrs,
       case job when 'PRESIDENT' then ename end as prez,
       case job when 'ANALYST' then ename end as anals,
       case job when 'SALESMAN' then ename end as sales
  from emp;

在这里插入图片描述

17、按时间分组

问题:你想按时段汇总数据。例如,你有一个交易日志,你想以5 秒为间隔对交易数据进行分组和汇总。TRX_LOG 表包含的数据行如下所示。

create table trx_log(
	TRX_ID INTEGER,
	TRX_DATE DATE,
	TRX_CNT INTEGER
);

insert into trx_log values
(1, '28-JUL-2020 19:03:07',44),
(2, '28-JUL-2020 19:03:08',18),
(3, '28-JUL-2020 19:03:09',23),
(4, '28-JUL-2020 19:03:10',29),
(5, '28-JUL-2020 19:03:11',27),
(6, '28-JUL-2020 19:03:12',45),
(7, '28-JUL-2020 19:03:13',45),
(8, '28-JUL-2020 19:03:14',32),
(9, '28-JUL-2020 19:03:15',41),
(10,'28-JUL-2020 19:03:16',15),
(11,'28-JUL-2020 19:03:17',24),
(12,'28-JUL-2020 19:03:18',47),
(13,'28-JUL-2020 19:03:19',37),
(14,'28-JUL-2020 19:03:20',48),
(15,'28-JUL-2020 19:03:21',46),
(16,'28-JUL-2020 19:03:22',44),
(17,'28-JUL-2020 19:03:23',36),
(18,'28-JUL-2020 19:03:24',41),
(19,'28-JUL-2020 19:03:25',33),
(20,'28-JUL-2020 19:03:26',19);

而你想返回如下结果集。

GRP TRX_START            TRX_END                   TOTAL
--- -------------------- -------------------- ----------
  1 28-JUL-2020 19:03:07 28-JUL-2020 19:03:11        141
  2 28-JUL-2020 19:03:12 28-JUL-2020 19:03:16        178
  3 28-JUL-2020 19:03:17 28-JUL-2020 19:03:21        202
  4 28-JUL-2020 19:03:22 28-JUL-2020 19:03:26        173

解决方案:将全部数据按条目进行分桶,每桶包含 5 行数据。实现这种逻辑分组的方式有多种,本实例使用的是 7 节介绍的方法,即用 TRX_ID 值除以 5。

创建好“分组”后,使用聚合函数 MIN、MAX 和 SUM 找出每个分组的起始时间、终止时间和交易总数。​(在 SQLServer 中,应该使用 CEILING 而不是 CEIL。​)

select ceil(trx_id/5.0) as grp,
       min(trx_date)    as trx_start,
       max(trx_date)    as trx_end,
       sum(trx_cnt)     as total
from trx_log
group by ceil(trx_id/5.0);

 grp | trx_start  |  trx_end   | total 
-----+------------+------------+-------
   3 | 2020-07-28 | 2020-07-28 |   202
   1 | 2020-07-28 | 2020-07-28 |   141
   4 | 2020-07-28 | 2020-07-28 |   173
   2 | 2020-07-28 | 2020-07-28 |   178
(4 rows)

18、同时对不同的分组/分区进行聚合

问题:你想同时聚合不同的维度。例如,你想返回一个结果集,其中列出了每位员工的名字、所属部门、所属部门的员工数、所属职位的员工数以及整个 EMP 表中的员工总数。结果集如下所示。

ENAME  DEPTNO DEPTNO_CNT JOB        JOB_CNT  TOTAL
------ ------ ---------- --------- -------- ------
MILLER     10          3 CLERK            4     14
CLARK      10          3 MANAGER          3     14
KING       10          3 PRESIDENT        1     14
SCOTT      20          5 ANALYST          2     14
FORD       20          5 ANALYST          2     14
SMITH      20          5 CLERK            4     14
JONES      20          5 MANAGER          3     14
ADAMS      20          5 CLERK            4     14
JAMES      30          6 CLERK            4     14
MARTIN     30          6 SALESMAN         4     14
TURNER     30          6 SALESMAN         4     14
WARD       30          6 SALESMAN         4     14
ALLEN      30          6 SALESMAN         4     14
BLAKE      30          6 MANAGER          3     14

解决方案:使用窗口函数 COUNT OVER,并指定要进行聚合的数据分区(分组)​。

select ename,
		 deptno,
		 count(*)over(partition by deptno) deptno_cnt,
		 job,
		 count(*)over(partition by job) job_cnt,
		 count(*)over() total
from emp;

在这里插入图片描述

19、聚合移动值区间

问题: 你想计算移动聚合,比如 EMP 表的移动薪水总计。你想从第一位员工的 HIREDATE 开始,计算 90 天内的薪水总计,以便了解从第一位员工获聘到最近一位员工获聘期间,90 天内的薪水波动情况。你想返回如下结果集。

HIREDATE        SAL SPENDING_PATTERN
----------- ------- ----------------
17-DEC-200      800              800
20-FEB-2011    1600             2400
22-FEB-2011    1250             3650
02-APR-2011    2975             5825
01-MAY-2011    2850             8675
09-JUN-2011    2450             8275
08-SEP-2011    1500             1500
28-SEP-2011    1250             2750
17-NOV-2011    5000             7750
03-DEC-2011     950            11700
03-DEC-2011    3000            11700
23-JAN-2012    1300            10250
09-DEC-2012    3000             3000
12-JAN-2013    1100             4100

解决方案:如果你使用的 RDBMS 支持相关的窗口函数,并允许在框架或窗口子句中指定移动窗口,那么这个问题解决起来将易如反掌。关键是在窗口函数中按 HIREDATE 排序,然后再指定一个 90 天的移动窗口(起点为第一位员工的获聘日期)​。计算的总薪水为当前员工获聘日期前 90 天内所有获聘员工(包括当前员工)的薪水总和。如果没有可供使用的窗口函数,则可以使用标量子查询,但解决方案将更复杂。

DB2 和 Oracle:在 DB2 和 Oracle 中,使用窗口函数 SUM OVER,并按HIREDATE 排序。在窗口或框架子句中,指定 90 天的区间,以计算当前员工以及之前 90 天内获聘的其他所有员工的薪水总和。在 DB2 中,不能在窗口函数的 ORDERBY 子句中指定 HIREDATE,必须按 DAYS(HIREDATE)排序,如下述代码的第 3 行所示。

select hiredate,
       sal,
       sum(sal)over(order by days(hiredate)
                     range between 90 preceding
                       and current row) spending_pattern
from emp e;

相比于 DB2 解决方案,Oracle 解决方案更简单,因为在 Oracle 中,可以在窗口函数中将日期时间数据类型作为排序依据。

select hiredate,
       sal,
       sum(sal)over(order by hiredate
                       range between 90 preceding
                         and current row) spending_pattern
from emp e;

MySQL:使用窗口函数 SUM OVER,但语法稍有不同。

select hiredate,
         sal,
         sum(sal)over(order by hiredate
             range interval 90 day preceding ) spending_pattern
from emp e;

PostgreSQL 和 SQL Server:使用标量子查询来计算当前员工获聘日期前 90 天内获聘的所有员工的薪水总和。

select e.hiredate,
       e.sal,
       (select sum(sal) from emp d
        where d.hiredate between e.hiredate-90
                                and e.hiredate) as spending_pattern
from emp e
order by 1;

20、转置包含小计的结果集

问题:你想制作一张包含小计的报表,再对其进行转置,以生成可读性更强的报表。例如,你被要求制作一张报表,其中列出了每个部门、每个部门的管理者以及各个管理者下属的员工的薪水总和。另外,你还想返回两种小计:每个部门由管理者管理的员工的薪水总和;结果集中的所有薪水之和(部门小计之和)​。当前,你已经有如下报表:

DEPTNO        MGR        SAL
------ ---------- ----------
    10       7782       1300
    10       7839       2450
    10                  3750
    20       7566       6000
    20       7788       1100
    20       7839       2975
    20       7902        800
    20                 10875
    30       7698       6550
    30       7839       2850
    30                  9400
                       24025

但要提供一张可读性更强的报表,因此要将上面的结果集转换成下面这样,让报表的含义更清晰。

MGR      DEPT10     DEPT20     DEPT30      TOTAL
---- ---------- ---------- ---------- ----------
7566          0       6000          0
7698          0          0       6550
7782       1300          0          0
7788          0       1100          0
7839       2450       2975       2850
7902          0        800          0
           3750      10875       9400      24025

解决方案:首先,使用 GROUP BY 扩展 ROLLUP 来生成小计。然后,执行经典转置操作(使用聚合和 CASE 表达式)来生成所需的报表列。使用函数 GROUPING 可以轻松地确定哪些值为小计(由 ROLLUP 生成的)​。根据你使用的RDBMS 对 NULL 值的排序方式,可能需要在解决方案中添加 ORDER BY,让最终的输出类似于前面的结果集。

DB2 和 Oracle:使用 GROUP BY 扩展 ROLLUP 来生成小计,然后使用CASE 表达式将数据转换为可读性更强的报表。

select mgr,
       sum(case deptno when 10 then sal else 0 end) dept10,
       sum(case deptno when 20 then sal else 0 end) dept20,
       sum(case deptno when 30 then sal else 0 end) dept30,
       sum(case flag when '11' then sal else null end) total
  from (
 select deptno,mgr,sum(sal) sal,
       cast(grouping(deptno) as char(1))||
       cast(grouping(mgr) as char(1)) flag
  from emp
 where mgr is not null
 group by rollup(deptno,mgr)
       ) x
 group by mgr;

SQL Server:使用 GROUP BY 扩展 ROLLUP 来生成小计,然后使用CASE 表达式将数据转换为可读性更强的报表。

 select mgr,
       sum(case deptno when 10 then sal else 0 end) dept10,
       sum(case deptno when 20 then sal else 0 end) dept20,
       sum(case deptno when 30 then sal else 0 end) dept30,
       sum(case flag when '11' then sal else null end) total
  from (
 select deptno,mgr,sum(sal) sal,
       cast(grouping(deptno) as char(1))+
       cast(grouping(mgr) as char(1)) flag
  from emp
 where mgr is not null
 group by deptno,mgr with rollup
       ) x
 group by mgr;

PostgreSQL:使用 GROUP BY 扩展 ROLLUP 来生成小计,然后使用CASE 表达式将数据转换为可读性更强的报表。

 select mgr,
        sum(case deptno when 10 then sal else 0 end) dept10,
        sum(case deptno when 20 then sal else 0 end) dept20,
        sum(case deptno when 30 then sal else 0 end) dept30,
        sum(case flag when '11' then sal else null end) total
    from (
  select deptno,mgr,sum(sal) sal,
         concat(cast (grouping(deptno) as char(1)),
         cast(grouping(mgr) as char(1))) flag
  from emp
  where mgr is not null
  group by rollup (deptno,mgr)
       ) x
  group by mgr;

 mgr  | dept10 | dept20 | dept30 | total 
------+--------+--------+--------+-------
 7839 |   2450 |   2975 |   2850 |      
      |   3750 |  10875 |   9400 | 24025
 7902 |      0 |    800 |      0 |      
 7698 |      0 |      0 |   6550 |      
 7788 |      0 |   1100 |      0 |      
 7782 |   1300 |      0 |      0 |      
 7566 |      0 |   6000 |      0 |      
(7 rows)

MySQL:使用 GROUP BY 扩展 ROLLUP 来生成小计,然后使用CASE 表达式将数据转换为可读性更强的报表。

select mgr,
		 sum(case deptno when 10 then sal else 0 end) dept10,
		 sum(case deptno when 20 then sal else 0 end) dept20,
		 sum(case deptno when 30 then sal else 0 end) dept30,
		 sum(case flag when '11' then sal else null end) total
from (
	select deptno,mgr,sum(sal) sal,
			concat( cast(grouping(deptno) as char(1)) ,
			cast(grouping(mgr) as char(1))) flag
	from emp
	where mgr is not null
	group by deptno,mgr with rollup
) x
group by mgr;

在这里插入图片描述

组态王电机运行时长自动记录方案:内存变量+脚本+报表协同实现秒级归档 在工业自动化监控系统中,设备运行时长统计是能耗分析、维保管理和故障追溯的基础能力。其技术本质是基于实时状态采集、时间戳计算与结构化存储的闭环逻辑,核心依赖内存变量暂存上下文、脚本引擎完成秒级时间运算(如HTConvertTime)、报表控件实现本地化展示与导出。该方案不依赖外部数据库或OPC服务器,适用于组态王6.55及以上版本,特别契合中小型项目对低成本、高可靠、易部署的诉求。通过启动/停止事件驱动、防抖校验与断电续写等工程实践,可稳定支撑电机启停记录、跨班次统计及审计级原始数据留存,是替代MES轻量级台 阅读详情

相关推荐

组态王电机运行时间记录方案:内存变量+脚本+报表精准追踪启停周期

在工业监控系统中,设备运行时间记录是基础但关键的数据需求,其本质是将离散状态变化转化为带时间戳的生命周期事件。原理上需解决三个核心问题:状态跳变捕获、时间差精确计算、结构化数据持久化。组态王原生报表虽不支持自动历史归档,但通过内存整形变量锚定行号、自定义脚本控制写入时序、字符串格式化保障时间解析一致性,可实现秒级精度的启停记录。该方案无需外部数据库或OPC服务器,适用于中小型PLC项目,特别契合暖通、泵阀等对运行健康度有统计需求的场景。文中详述的‘3变量建模’与‘报表单元格级写入’正是落地该技术路径的核心实

weixin_29163797的博客 30

报表设计中的几个SQL技巧

使用专业的报表工具设计报表模板时,数据准备是一个关键环节。数据准备的核心,实际上是数据库查询SQL语句的编写。 在一个典型的交叉分析报表中,需要显示每个大区、每个月度的销售业绩,如下图: 这个报表的数据集查询语句大致如下: select DATEPART(month,订购日期) 月份 , 销售大区 , SUM(订单金额) 销售额 from Demo_销售明细 where 订购日期 &gt...

williamluo67的专栏 4326

组态王电机运行时长自动记录与报表生成方案

工业自动化中,设备运行时长统计是能源管理、预防性维护和技改验收的基础需求。其本质是基于状态机的时间戳采集、秒级差值计算与时序闭环控制。组态王虽无内置累计器,但可通过内存整形变量实现行号锚定、脚本触发启停事件、HTConvertTime函数完成日历时间到秒的高精度转换,并结合ReportSetCellString写入结构化报表。该方案规避了外部数据库依赖,适配6.55/7.5等主流版本,支持跨天、跨月运行时长格式化(X天Y时Z分W秒)及Excel一键导出,广泛应用于泵阀类电机的运维系统与能效计量模块。

weixin_29168153的博客 234

SQL Server报表——小白开发笔记

此文章为个人原创,转载请注明原网址。 这几天被安排用SQL Server自带的报表服务器做个报表,踩了好几个坑后,这里记录一下一点经验。。。所用到的软件是SQL2014自带的ReportBuilder。 1、报表开发第一步是配置环境,其实就是配置数据源,就是这东东,SQLServer安装好后自带的,打开进入下图这个页面,然后点击URL网址 接着弹出一个网页,然后新建数据源...

qq_41873966的博客 9133

Intouch 2014 R2 数据写库与Excel报表及数据库恢复实战

SCADA系统数据存储与报表生成是工业自动化领域的核心需求。通过SQL Server数据库实现实时数据持久化,结合Excel VBA构建班报日报,并掌握数据库备份与附加恢复技术,可有效保障历史数据可追溯性。在产线数据存贮、历史数据恢复等场景中,合理配置数据库连接与写入脚本、优化报表查询逻辑、规范文件恢复流程,是提升系统可靠性的关键。本文围绕Intouch 2014 R2与SQL Server 2012的集成,详细解析从写库到报表再到恢复的完整链路,助力工控从业者高效落地数据管理方案。

weixin_32821533的博客 30

Intouch到SQL Server数据链路搭建:解决报表缺失与补录问题

工业现场的数据归档与报表生成,常因HMI与历史数据库之间的链路不稳定而出现数据补录和报表缺失。Intouch作为主流SCADA组态软件,通过SQLConnect与SQL Server建立连接,并借助绑定列表完成标记点到表字段的映射,配合条件脚本实现定时自动写库。这一机制将实时数据稳定落入关系型数据库,为Excel报表系统提供可靠的数据源,从根本上减少人工补录与核对。本文详细讲解从SQL Server身份验证、建表规范,到Intouch绑定列表配置、连接串写法,再到防重写逻辑和常见故障排查的完整路径,适合SC

weixin_30512965的博客 147

SQL常用函数及使用案例

SQL 常用函数 CONCAT 连接函数 分组排序函数 MERGE函数等

Aroundchange 9227

Kali实战:构建安卓远程监控工具,实现拍照、定位与信息获取

本文详细介绍了如何使用Kali Linux构建安卓远程监控工具,实现拍照、定位与信息获取功能。通过Metasploit框架生成安卓载荷,部署到目标设备并建立连接,执行远程监控操作如获取位置信息、拍照和读取通讯录。文章还提供了进阶技巧与问题排查方法,强调法律与道德考量。

weixin_28716443的博客 239

SQL的一些常见案例

本书第﹖版以最新的Ubuntu 12.04为版本,循序渐进地向读者介绍了Linux 的基础应用、系统管理、网络应用、娱乐和办公、程序开发、服务器配置、系统安全等。在如 MySQL 不支持开窗函数时,可以用第二种方法——自连接,用表a控制求和的位置,用表b的x求和(只用一个表就统一都是截至某行的sum值了,必须要自连接)需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论。这里的假设需要ID是连续的。

2401_86402111的博客 1227

别再傻傻分不清PBR和NPR了!从《原神》到《双城之战》,聊聊游戏里那些让你眼前一亮的渲染风格

本文深入解析了PBR(基于物理的渲染)与NPR(非真实感渲染)在游戏视觉风格中的应用差异,通过《原神》和《双城之战》的案例,展示了两种渲染技术如何创造出截然不同的艺术效果。从技术原理到实战案例,帮助读者理解如何根据项目需求选择合适的渲染风格。

weixin_30425949的博客 146

Skiplist,B,B+tree,LSM tree

SkipList 理解(如何实现): 即使是有序的链表,查找的时间复杂度也是O(n),但链表不能随机访问所以不适用于二分查找,使得查找的时间复杂度为O(logn)。 于是将有序链表和二分查找结合得到了SkipList,类似于二叉搜索树,这是一种以空间换时间的算法,通过在每个节点中增加向前的指针,从而提升查找效率(如下图所示)。 一开始如a所示,是一个有序的链表,时间复杂度O(n)。随后建立了e所示的SkipList,从最顶层往下查找,可以跳过许多元素,减少查找次数。 ​

weixin_45875458的博客 947

sql 考勤月报表_不想再被报表需求烦到炸,4个妙招让你实现批量制表

对于经常制作报表的人来说,最痛苦的事情莫过于漫天的复杂报表需求了:每天来自各个部门的不同需求千奇百怪,什么绩效表、提升表、考勤表、报销款项表、日报周报等等,最最折磨人的是还需要进行分类汇总、对比汇总、明细汇总等,堆成山的报表需求几乎占据了报表人的所有时间(包括加班)。最近就有一个做财务的朋友跟我抱怨,每个月做的报表不说可以绕地球几圈,关键是重复报表制作起来相当的枯燥机械,天天对着Excel取数汇总...

weixin_30002151的博客 765

LTspice进阶技巧-031.一键加密仿真模型:LTspice Encrypt Tool实战指南

本文详细介绍了LTspice Encrypt Tool的使用方法,帮助工程师一键加密LTspice仿真模型,保护知识产权。通过图形化界面简化操作流程,支持批量加密和兼容性处理,适用于电路设计中的模型保护需求。

weixin_28304023的博客 202

数据库语法总结(12)——报表的制作和整形

数据库语法总结(12)——报表的制作和整形

m0_61746796的博客 1542

浅显易懂地看LLC变换器(三)

第3期 谐振与增益的定性关系 由简单的谐振电路说起 我们将以电感和电容的基本电路的特性逐渐了解谐振电路的特性 与电阻不同,电感和电容都不是纯阻性线性器件,电感的感抗XL和电容的容抗Xc都与频率有关,当加在电感和电容上的频率发生变化时,它们的感抗XL和容抗Xc会发生变化。 1) 如下图RL电路,当输入源Vin的频率增加时,电感的感抗增大,输出电压减小,增益Gain=Vo/Vin随频率增加而减小。 2) 如下图RC电路,相反,当输入源Vin的频率增加时...

iFTrue电力电子专栏 6735

【CQUT】从零开始龙芯杯-开始前的准备

不同级别的Cpu对于vivado综合的时间有较大影响,其中Cpu的单核、多核性能以及cache性能影响权重较高。因此,为了保证学习进度和效率,我们建议在Cpu不低于I5 12400的环境下进行学习。Vivado2023.2安装包约100GB,为了保证成功安装建议留出300GB硬盘空间。龙芯杯的许多资料被托管在gitee,你需要git指令将资料克隆到本地。指定用书:CPU设计实战 Loongarch版 作者:汪文祥、邢金璋。Cpu性能对比网站:https://www.cpu114.com/

weixin_73689971的博客 906

SQL报表之户籍管理实例

 /*统计局户籍管理实例*/  create table person  (  sid int primary key identity(1000,1),  age int,  sex varchar(10),  ismarriged varchar(10)  )  insert into person select 20,male,true  union select 31,femal

风情路96号 822

redhawk:avgTW maxTW minTW minWC的区别

分析dynamic ir drop报告时会遇到这几个值,avgTW maxTW minTW minWC,如下图。我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧?

m0_61544122的博客 691

水晶报表的使用:

水晶报表的使用:  在设计好水晶报表后添加到VB的设计环境里,如图是设计好的云南昆石高速公路报表之一    然后再Param2字段上点击右健,选择格式字段,弹出格式化编辑器的对话框,注意Param2字段是整形数,我们要把它格式化成不同的字符串。点击完毕出现设计公式工作室,如此继续对Param2进行编辑: 代码如下:      if {命令.EventTypeId}=53 and {命令.Param

qianbo_0423的专栏 qq:418511899 18710866728 1575

win7 vs2012+wdk8.0 搭建wdf驱动开发环境

开发环境搭建: 系统:win7 x64 工具:vs2012 + WDK8.0 插件:wdfcoinstaller.msi (1)先安装vs2012,再安装wdk8.0,这样在打开vs2012时可以创建windows drivers类型的工程。 注:vs2012不能安装wdk8.1,wdk8.1必须安装在vs2013上 下载链接:http://msdn.microsoft.com/zh-cn/win...

技术联盟 5342

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

盈帆报表设计器简介

盈帆报表设计器用来采集数据、加工数据、整理数据,把数据做成分组报表、交叉表、多层交叉表、明细表、主从报表等不同的报表,让数据变得更加直观、更加简洁的数据。报表保存后成为模板,模板上传报表平台上就可以通过HTML识别,方便用户通过浏览器查看、打印和导出数据。 下图是盈帆报表设计器的主界面,可以分为7个部分:1、菜单栏 2、模板存放区 3、数据源区 4、工具栏 5、工作表6、属性栏 7、格式栏。 ...

efreport的博客 676

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

FR报表制作实战:从零到精通FCP认证中的复杂填报与大屏设计

本文为FCP认证备考者及报表开发者提供FR报表制作的实战指南,重点解析复杂填报与大屏设计。内容涵盖从SQL查询优化、Kettle数据ETL,到主子表关联填报、数据校验,再到动态大屏的交互设计与性能优化,旨在帮助读者构建从数据准备到前端展示的完整能力闭环,高效应对FCP认证与实际项目挑战。

qsc90123456的博客 182

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

javaEcharts报表管理器

javaEcharts报表管理器是基于Echarts开发,通过配置数据源映射相应的Echarts报表 JavaEcharts报表管理器主要特点: 支持一维报表、二维报表 支持报表的图文并茂 支持报表导出Excel 支持报表汇总...

chuangjiji0839的博客 215

用MySQL玩转数据可视化:SQL整形、性能优化与看板实战

数据可视化旨在将复杂业务数据转化为直观图表,而高效图表的前提是数据准备与查询性能。SQL作为数据库交互的核心语言,既能完成数据清洗、聚合与透视,也能直接产出图表所需的结构化结果集。掌握MySQL中的时间归一化、条件聚合、窗口函数等技巧,可显著缩短数据加工链路;针对图表加载慢的问题,通过索引优化、执行计划分析和合理的汇总表设计,往往能与专用大数据框架相媲美。从个人销售看板到企业级报表,MySQL与ECharts的结合提供了一条轻量高效的数据可视化落地路径。

weixin_26833939的博客 310

『行式报表』通过方法对数据进行过滤

(关于Scriptcase产品,请查看 http://www.phpscriptcase.com 查看相关功能) Scriptcase提供了方便的方式直接创建行式报表,能够一键生成简单、直接的报表。Scriptcase会依据数据机构,自己主动推断其数据类型,而且生成对应格式的字段...

aoe41606的博客 130

MySQL数据可视化实战:窗口函数、JSON聚合与生成列打造报表SQL

在数据可视化链路中,真正耗时往往不是图表渲染,而是数据整形。MySQL 8.0 的窗口函数、JSON_ARRAYAGG 和生成列,能把趋势、同环比、漏斗留存等口径在数据库层直接算好,让前端或报表工具只做最后一层渲染。理解窗口函数的ROWS边界、利用JSON聚合输出图表结构、借助生成列提取JSON字段参与统计,再配合日期补零与预聚合,可以沉淀为可复用的报表SQL。适用于中小规模固定报表、内部管理看板和后端数据接口,兼顾查询性能与口径统一。通过SQL输出SVG和ASCII条形图,还能实现轻量化数据自检。

weixin_32822843的博客 136
上一篇: SQL经典实例——高级查找
下一篇: SQL经典实例——分层查询
吴声子夜歌
博客等级 码龄9年 2687粉丝 2262原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值