【问题标题】:SQL 2014 - amalgamating rows into single row, multiple columns with CASE statementsSQL 2014 - 使用 CASE 语句将行合并为单行、多列
【发布时间】:2017-12-15 19:36:37
【问题描述】:

使用 T-SQL 的 SQL Server 2014。

在我们的数据库中,客户有问题(!),分配给客户的每个问题在我正在查询的表中都有一个单独的行。

我希望为这些问题中的每一个(以及其他一些信息)创建一个单行,但目前,我的每个 CASE 语句都创建一个单行。

所以输出是这样的(我已经更改了一些标题名称,以便它们适合这里):

Prod    Date        Time    Cust    Lname   Fname   Street 1          S2    City            Zip     Email               CSI     Action  Dog Prog    TourHeadsets
Tosca   08-Apr-17   2:30 PM 122253  Smith   Michael 33 Rodeo Drive    NULL  Beverley Hills  90210   msmith@email.com    NULL    NULL    0   None    0   1
Tosca   08-Apr-17   2:30 PM 122253  Smith   Michael 33 Rodeo Drive    NULL  Beverley Hills  90210   msmith@email.com    NULL    NULL    0   Large   0   0
Tosca   08-Apr-17   2:30 PM 122253  Smith   Michael 33 Rodeo Drive    NULL  Beverley Hills  90210   msmith@email.com    NULL    NULL    0   None    2   0
Tosca   08-Apr-17   2:30 PM 125634  Brown   Sarah   22 Victory Drive  NULL  Beverley Hills  90210   sbrown@email.com    NULL    NULL    0   Large   0   0
Tosca   08-Apr-17   2:30 PM 125634  Brown   Sarah   22 Victory Drive  NULL  Beverley Hills  90210   sbrown@email.com    NULL    NULL    0   None    2   0
Tosca   08-Apr-17   2:30 PM 125634  Brown   Sarah   22 Victory Drive  NULL  Beverley Hills  90210   sbrown@email.com    NULL    NULL    0   None    0   2

但我想要这个(4 个 CASE 语句的结果在同一行的不同列中):

Prod Date       Time    Cust    Lname   Fname   Street 1         S2  City   Zip Email   CSI Action  Dog Prog    Tour    Headsets
Tosca   08-Apr-17   2:30 PM 122253  Smith   Michael 33 Rodeo Drive   NULL   Beverley Hills  90210   msmith@email.com    NULL    NULL    0   Large print 2   1
Tosca   08-Apr-17   2:30 PM 125634  Brown   Sarah   22 Victory Drive NULL   Beverley Hills  90210   sbrown@email.com    NULL    NULL    0   Large print 2   2

对新手有什么帮助吗?! (也非常欢迎对代码提供任何其他反馈。我对此完全陌生。)这是我一直使用的代码:

USE impresario
    SELECT 
    g.description AS 'Production'
    ,CONVERT(varchar,f.perf_dt,106) AS 'Date'
    ,FORMAT(CAST(f.perf_dt AS DATETIME),'h:mm tt') AS 'Time'
    ,a.customer_no AS 'Customer'
    ,b.lname AS 'Last name'
    ,b.fname AS 'First name'
    ,c.street1 AS 'Street 1'
    ,c.street2 AS 'Street 2'
    ,c.city AS 'City'
    ,c.postal_code AS 'Postal code'
    ,d.address
    ,a.notes AS 'CSI notes' 
    ,e.notes AS 'Action notes'
    ,CASE h.id
       WHEN 14 THEN '1'
       WHEN 15 THEN '2'
       ELSE '0'
       END
       AS 'Dogs'
    ,CASE h.id
       WHEN 16 THEN 'Large print'
       WHEN 17 THEN 'Braille'
       ELSE 'None'
       END
       AS 'Programmes'
    ,CASE h.id
       WHEN 18 THEN '1'
       WHEN 19 THEN '2'
       ELSE '0'
       END
       AS 'Touch tour'
    ,CASE h.id
       WHEN 20 THEN '1'
       WHEN 21 THEN '2'
       ELSE '0'
       END
       AS 'Headsets'
      FROM T_CUST_ACTIVITY a
      JOIN T_CUSTOMER b ON b.customer_no=a.customer_no
      JOIN T_ADDRESS c ON c.customer_no=a.customer_no
      JOIN T_EADDRESS d ON d.customer_no=a.customer_no
      JOIN T_ISSUE_ACTION e ON e.activity_no=a.activity_no
      JOIN T_PERF f ON f.perf_no=a.perf_no
      JOIN T_INVENTORY g ON g.inv_no=f.prod_season_no
      JOIN TR_ACTION h ON h.id=e.action
      WHERE a.activity_type=21 --'Access requirements' from TR_CUST_ACTIVITY_TYPE
      AND c.primary_ind='Y' --Primary addresses only
      AND d.primary_ind='Y' --Primary emails only
      AND e.action IN 
      (
       14 --Dog x1
      ,15 --Dog x2
      ,16 --Programme (large print)
      ,17 --Programme (braille)
      ,18 --Touch tour x1
      ,19 --Touch tour x2
      ,20 --Headset x1
      ,21 --Headset x2
      )
      ORDER BY f.perf_dt, a.customer_no ASC

【问题讨论】:

  • 看起来您的TR_ACTION 表可能返回了多行。检查您的数据并查看哪些行被重复。此外,我更喜欢将我的 JOIN 条件放在实际的 JOIN 中,而不是在 where 中。即JOIN T_ADDRESS c ON c.customer_no = a.customer_no AND c.primary_ind='y'。在 JOIN 之外有一个条件将在您的 ON 条件下将 tableA 连接到 tableB,然后根据您的 WHERE 删除行。这会使连接的数据更大,有时会导致返回意外的行,尤其是在 OUTER JOIN 中。以集合的形式考虑您的数据会有所帮助。
  • 这对 JOIN 非常有帮助 - 谢谢。它在视觉上看起来也不那么混乱。就像你说的那样,在集合中思考它肯定会有所帮助。关于多行,该表确实为每个操作/问题都有一行,我猜这就是为什么它们在查询中作为单独的行出现。理想情况下,我想以一种我将其描述为 Excel 数据透视表的新手方式将这些行与共同点(客户)合并,然后将每个操作/问题放在其右侧的不同单元格中。
  • 查询的更新取决于你在不同场景下想要做什么的很多条件。当 Michael smith 有另一排(第 4 排)prog = braille、dog = 1、touch tour = 1 和耳机 = 2 时,你想显示什么?您想合并成 2 行吗?这里的选择标准是什么?
  • 有了轴心,你必须知道你希望你的最终结构是什么样子。但正如 CuriousKid 所说,如果返回的行数超过 2 行怎么办?如果您只需要子查询中的 1 个结果,则可以使用 ROW_NUMBER() 窗口函数或 TOP 1 或类似的东西来确保每条基本记录只返回一行。
  • 感谢您的意见。我希望将当前 CASE 场景中返回的任何数据合并为每个客户 1 行。因此,每个客户的狗/耳机/旅行/节目组合是什么并不重要;只是它们被包含在合并线上的狗/耳机/旅游/节目单元格中,如果这有意义的话。

标签: sql tsql case sql-server-2014


【解决方案1】:

您可以在 cte 中使用您的查询并使用 row_number 来获得如下结果:

;With Cte as (
    --your full query
), Cte2 as (
Select *, RowN = Row_Number() over(partition by Lname, Fname order by [Tour] desc)
    , [HeadSetsMax] = max(headsets) over(Partition by Lname, Fname)
    from Cte
    )
    Select * from Cte2 where RowN = 1

【讨论】:

  • 为什么不直接使用 group by?在没有增加价值的情况下似乎更加复杂。
  • 这纯粹是根据要求,如果同一个人有两条不同地址的记录会发生什么情况,在这种情况下,Row_Number 不会重复记录,而 group by 会重复。所以这取决于OP的要求
  • 是的,为了避免单人出现两行,我使用 row_number 方法而不是 group by...
  • 我想念你的评论——一个组究竟是如何重复的?
【解决方案2】:

当遇到此类问题时,我会这样做——使用子查询或 CTE 对要组合的项目进行分组。如果没有关于数据模型和边缘案例的更多详细信息,很难确定您到底需要什么。 (@CuriousKid 在他的 cmets 中给出了一些关于这些案例的提示。)但我对最常见的用例做了假设。

注意,我取出了不需要的连接并清理了一些可怕的格式。

 WITH actions AS
 (
    SELECT
      activity_no,
      MAX(CASE e.action WHEN 14 THEN '1' WHEN 15 THEN '2' ELSE null END) AS dogs,
      MAX(CASE e.action WHEN 16 THEN 'Large print' WHEN 17 THEN 'Braille' ELSE null END) AS programmes,
      MAX(CASE e.action WHEN 20 THEN '1'  WHEN 21 THEN '2' ELSE null END AS headsets
      MAX(CASE e.action WHEN 18 THEN '1' WHEN 19 THEN '2' ELSE null END) AS tt,
      LISTAGG(e.notes, ', ') as notes
    FROM T_ISSUE_ACTION e
    WHERE e.action IN (
       14 --Dog x1
      ,15 --Dog x2
      ,16 --Programme (large print)
      ,17 --Programme (braille)
      ,18 --Touch tour x1
      ,19 --Touch tour x2
      ,20 --Headset x1
      ,21 --Headset x2
    )
    GROUP BY activity_no
 )
 SELECT 
    g.description AS 'Production'
    ,CONVERT(varchar,f.perf_dt,106) AS 'Date'
    ,FORMAT(CAST(f.perf_dt AS DATETIME),'h:mm tt') AS 'Time'
    ,a.customer_no AS 'Customer'
    ,b.lname AS 'Last name'
    ,b.fname AS 'First name'
    ,c.street1 AS 'Street 1'
    ,c.street2 AS 'Street 2'
    ,c.city AS 'City'
    ,c.postal_code AS 'Postal code'
    ,d.address
    ,a.notes AS 'CSI notes' 
    ,e.notes AS 'Action notes'
    ,e.dogs AS 'Dogs'
    ,e.programmes AS 'Programmes'
    ,e.tt AS 'Touch tour'
    ,e.headset AS 'Headsets'
 FROM T_CUST_ACTIVITY a
 JOIN T_CUSTOMER b ON b.customer_no=a.customer_no
 JOIN T_ADDRESS c ON c.customer_no=a.customer_no  AND c.primary_ind='Y' --Primary addresses only
 JOIN T_EADDRESS d ON d.customer_no=a.customer_no   AND d.primary_ind='Y' --Primary emails only
 LEFT JOIN actions e ON e.activity_no=a.activity_no
 JOIN T_PERF f ON f.perf_no=a.perf_no
 JOIN T_INVENTORY g ON g.inv_no=f.prod_season_no
 WHERE a.activity_type=21 --'Access requirements' from TR_CUST_ACTIVITY_TYPE
 ORDER BY f.perf_dt, a.customer_no ASC

【讨论】:

  • 感谢您的意见(以及整理 - 抱歉,这里是新手!)。 LISTAGG 在 SQL Server 2014 中作为未知函数抛出错误。是否有任何替代方法可行?
  • 好点——STRING_AGG 将出现在 SQL Server 的未来版本中。这是(恕我直言)关于 SQL Server 替代方案的最佳文章。 sqlperformance.com/2014/08/t-sql-queries/…
【解决方案3】:

我找到了一种针对每个 CASE 语句使用 MAX 来解决此问题的方法,这似乎奏效了:

USE impresario
SELECT 
g.description AS 'Production'
,CONVERT(varchar,f.perf_dt,106) AS 'Date'
,FORMAT(CAST(f.perf_dt AS DATETIME),'h:mm tt') AS 'Time'
,a.customer_no AS 'Customer'
,b.lname AS 'Last name'
,b.fname AS 'First name'
,c.street1 AS 'Street 1'
,c.street2 AS 'Street 2'
,c.city AS 'City'
,c.postal_code AS 'Postal code'
,d.address
,a.notes AS 'CSI notes' 
,e.notes AS 'Action notes'
,MAX(CASE h.id
   WHEN 14 THEN '1'
   WHEN 15 THEN '2'
   ELSE '0'
   END)
   AS 'Dogs'
,MAX(CASE h.id
   WHEN 16 THEN 'Large print'
   WHEN 17 THEN 'Braille'
   ELSE 'None'
   END)
   AS 'Programmes'
,MAX(CASE h.id
   WHEN 18 THEN '1'
   WHEN 19 THEN '2'
   ELSE '0'
   END)
   AS 'Touch tour'
,MAX(CASE h.id
   WHEN 20 THEN '1'
   WHEN 21 THEN '2'
   ELSE '0'
   END)
   AS 'Headsets'
  FROM T_CUST_ACTIVITY a
  JOIN T_CUSTOMER b ON b.customer_no=a.customer_no
  JOIN T_ADDRESS c ON c.customer_no=a.customer_no
  JOIN T_EADDRESS d ON d.customer_no=a.customer_no
  JOIN T_ISSUE_ACTION e ON e.activity_no=a.activity_no
  JOIN T_PERF f ON f.perf_no=a.perf_no
  JOIN T_INVENTORY g ON g.inv_no=f.prod_season_no
  JOIN TR_ACTION h ON h.id=e.action
  WHERE a.activity_type=21 --'Access requirements' from TR_CUST_ACTIVITY_TYPE
  AND c.primary_ind='Y' --Primary addresses only
  AND d.primary_ind='Y' --Primary emails only
  AND e.action IN 
  (
   14 --Dog x1
  ,15 --Dog x2
  ,16 --Programme (large print)
  ,17 --Programme (braille)
  ,18 --Touch tour x1
  ,19 --Touch tour x2
  ,20 --Headset x1
  ,21 --Headset x2
  )
  GROUP BY 
  g.description
  , f.perf_dt
  , a.customer_no
  , b.lname
  , b.fname
  , c.street1
  , c.street2
  , c.city
  , c.postal_code
  , d.address
  , a.notes
  , e.notes
  ORDER BY 
  f.perf_dt
  , a.customer_no ASC

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-18
    • 1970-01-01
    • 2020-08-06
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    相关资源
    最近更新 更多