1

我想在字段中总共有一个特定的字符串,例如在PAYROLL_PAYMONTH字段中。例如,我将统计字段中“HELLO”的数量,并将其显示在一个组中。

-- DATA 
EMP_SURNAME   PAYROLL_PAYYEAR    PAYROLL_PAYMONTH
    X              1999                JAN
    X              1999                JAN
    X              1999                FEB

-- OUTPUT 
EMP_SURNAME   PAYROLL_PAYYEAR       JAN   FEB   MAR
    X              1999              2     1     0

为了计算字段中的相同字符串并显示它,我使用 SQL Manager for Firebird 在 Firebird 3 中创建了一个组选择过程

CREATE PROCEDURE PAID_LISTING(
  SORT_PAYROLL_YEAR VARCHAR(50) CHARACTER SET ISO8859_1 COLLATE ISO8859_1)
RETURNS(
  EMP_SURNAME VARCHAR(50) CHARACTER SET ISO8859_1 COLLATE ISO8859_1,
  PAYROLL_PAYYEAR VARCHAR(50) CHARACTER SET ISO8859_1 COLLATE ISO8859_1,
  PAYROLL_MON_JAN VARCHAR(50) CHARACTER SET ISO8859_1 COLLATE ISO8859_1)
AS
BEGIN
  FOR
    SELECT
      B.EMP_SURNAME,
      A.PAYROLL_PAYYEAR,
      COUNT (A.PAYROLL_PAYMONTH)

    FROM PAYROLL A, EMP B
    WHERE A.EMP_PK = B.EMP_PK AND  A.PAYROLL_YEAR =: SORT_PAYROLL_YEAR

    GROUP BY
      B.EMP_SURNAME,
      A.PAYROLL_PAYYEAR

    ORDER BY B.EMP_SURNAME ASC
    INTO
      :EMP_SURNAME,
      :PAYROLL_PAYYEAR,
      :PAYROLL_MON_JAN
  DO
    BEGIN
      SUSPEND;
    END
END;

但这不是我想要的结果。接下来做什么?

4

1 回答 1

1

您想要获得的称为“交叉表报告” - https://en.wikipedia.org/wiki/Crosstab

生成它的正常方法分为两个步骤:

  1. 您在数据库中进行通常的单向查询,列数固定,列在语义上不同,彼此不重复。因此,年份和月份 - JAN、FEB 等 - 将出现在不同的行中,而不是在相邻的列中。

  2. 然后您通过客户端应用程序在所需的二维表中表示该一维查询的结果。您使用哪些语言和库来制作您的客户端应用程序,它们应该提供从常规 1D 查询中创建交叉表的方法。

问题是,数据库是一个保存和提取数据的工具,而不是让它看起来很吸引人。您的客户端应用程序是一种工具,可以以易于查看的方式呈现数据。“Divide et empera”,将每个工具用于创建和优化的任务。尽管可能,强制 SQL 服务器进行可视化表示将是一种“追求荣耀”,既不自然,又相对缓慢。

但是,如果您打算在纯 SQL 中实现它,而不管它效率低下,那么您可以使用 CTE。

再次,“divide et empera”,将您的复杂任务拆分为更小更简单的任务。我将使用您在问题中输入的示例数据。

CREATE TABLE DATA (
  EMP_SURNAME VARCHAR(10) NOT NULL,
  PAYROLL_PAYYEAR SMALLINT NOT NULL,
  PAYROLL_PAYMONTH CHAR(3) NOT NULL);


/*
EMP_SURNAME   PAYROLL_PAYYEAR    PAYROLL_PAYMONTH
  X              1999                JAN
  X              1999                FEB
  X              1999                JAN
*/

你必须做三个步骤。

  1. 折叠数据 - 计算每月的行数。这是通常的GROUP BY查询,通常它是唯一的查询,因为交叉制表将由您的应用程序从其结果中完成。

  2. 制作结果表将包含的行的“骨架”列表。这意味着 - 有任何数据的所有对 PERSON+YEAR。这将跳过任何一个月都没有数据的年份。

  3. 将这些查询结果强制执行在一起,并使它们逐列水平对齐,而不是 SQL 行下行结构的正常对齐方式。

开始了。

步骤1:

 select EMP_SURNAME, PAYROLL_PAYYEAR, PAYROLL_PAYMONTH, Count(*) as QTY
 from DATA 
 group by EMP_SURNAME, PAYROLL_PAYYEAR, PAYROLL_PAYMONTH


EMP_SURNAME PAYROLL_PAYYEAR PAYROLL_PAYMONTH    QTY
X       1999        FEB                 1
X       1999        JAN                 2

第2步:

select distinct EMP_SURNAME, PAYROLL_PAYYEAR from DATA

EMP_SURNAME PAYROLL_PAYYEAR
X       1999

第 3 步:

with EMP_YEAR as ( select distinct EMP_SURNAME, PAYROLL_PAYYEAR from DATA )
,GROUPED as
(
  select EMP_SURNAME, PAYROLL_PAYYEAR, PAYROLL_PAYMONTH, Count(*) as QTY
  from DATA group by EMP_SURNAME, PAYROLL_PAYYEAR, PAYROLL_PAYMONTH
)

select EMP_YEAR.EMP_SURNAME, EMP_YEAR.PAYROLL_PAYYEAR
  ,coalesce( emp_jan.qty, 0) as JAN
  ,coalesce( emp_feb.qty, 0) as FEB
  ,coalesce( emp_mar.qty, 0) as MAR
from EMP_YEAR
left join GROUPED as EMP_JAN on
     EMP_YEAR.EMP_SURNAME = EMP_JAN.EMP_SURNAME and
     EMP_YEAR.PAYROLL_PAYYEAR = EMP_JAN.PAYROLL_PAYYEAR and
     EMP_JAN.PAYROLL_PAYMONTH = 'JAN'
left join GROUPED as EMP_FEB on
     EMP_YEAR.EMP_SURNAME = EMP_FEB.EMP_SURNAME and
     EMP_YEAR.PAYROLL_PAYYEAR = EMP_FEB.PAYROLL_PAYYEAR and
     EMP_FEB.PAYROLL_PAYMONTH = 'FEB'
left join GROUPED as EMP_MAR on
     EMP_YEAR.EMP_SURNAME = EMP_MAR.EMP_SURNAME and
     EMP_YEAR.PAYROLL_PAYYEAR = EMP_MAR.PAYROLL_PAYYEAR and
     EMP_MAR.PAYROLL_PAYMONTH = 'MAR'

...这就是你想要得到的:

EMP_SURNAME PAYROLL_PAYYEAR JAN FEB MAR
X           1999            2   1   0

现在,这个查询很丑,很脆弱(很多复制粘贴,你很容易犯错,然后很难发现它),而且 - 它很慢。只需查看此请求的查询计划- 您可以一次又一次地将表与每一列连接起来!

PLAN JOIN (JOIN (JOIN (SORT (EMP_YEAR DATA NATURAL), SORT (EMP_JAN DATA NATURAL)), SORT (EMP_FEB DATA NATURAL)), SORT (EMP_MAR DATA NATURAL))

所以...这就是您可以在 SQL 服务器中执行此操作的方式,但再想一想并尝试在适当的工具之间分配任务,因此仅在服务器上执行分组查询 #1,并使您的客户端应用程序将其重新组合成跨表报告

PS。在 Firebird 中将此查询包装到存储过程中并不是一个好主意。过程和函数用于编程。如果您只想将复杂查询保留为命​​名的 SQL 对象 - 这就是 SQL VIEW 的用途。

create view CTE_CROSSTAB (EMP_SURNAME,PAYROLL_PAYYEAR,JAN,FEB,MAR) as   
with EMP_YEAR as ( select distinct EMP_SURNAME, PAYROLL_PAYYEAR from data )
......etc
于 2018-01-16T09:28:47.427 回答