DB2将唯一列值连接成一行,逗号分隔

时间:2017-08-08 21:20:27

标签: sql ibm-midrange db2-400

两张桌子:

Parts Table:
Part_Number   Load_Date   TQTY
m-123         19940102    32
1234Cf        20010809    3
wf9-2         20160421    14


Locations Table:
PartNo     Condition     Location   QTY
m-123      U             A02        2
1234Cf     S             A02        3
m-123      U             B01        1
wf9-2      S             A06        7
m-123      S             A18        29
wf9-2      U             F16        7

Result:
Part_Number   Load_Date  TQTY   U_LOC    UQTY    S_LOC   SQTY
m-123         19940102   32     A02,B01  3       A18     29
1234Cf        20010809   3                       A02     3
wf9-2         20160421   14     F16      7       A06     7 

我无法使用当前的DB2版本找到解决方案。我不完全确定如何找到该版本,但它在AS400系统上运行,似乎DB2的版本与操作系统版本相关联。框中使用的是:操作系统:i5 / OS版本:V5R4M0 (我尝试了一些命令来使用这些建议来获取DB2版本Here但是没有一个像大多数人所说的那样有效。

关于将多行列数据连接成一行,我遇到过许多文章,声明使用 XMLAGG xmlserialize Here和{ {3}}但是我收到一条错误,指出命令无法识别。

不知道从哪里开始,因为似乎有解决方案,但我无法使这些已建议的功能起作用。

修改 使用接受的答案和解释,以及示例 Here通过一个简单的例子来了解递归的基本概念,它是
HERE使用" SELECT rownumber() over(partition by category)"这些陈述确实有助于将它们整合在一起。一旦我理解了这一陈述。

我还学会了确保递归中使用的数据尽可能缩小,然后再加上额外的数据。这使得结果呈指数级增长。 < - 这似乎很明显,但是当试图弄清楚所有这一切时,它并不明显,我的查询非常慢。一旦我理解了实际​​发生的事情,就可以更容易地对真正快速的结果进行调整。

2 个答案:

答案 0 :(得分:2)

这相当复杂,所以我将展示我的所有工作:

表定义

create table parts
  (part_number      Varchar(64),
   load_date        Date,
   total_qty        Dec(5,0));
create table locations
  (part_number      Varchar(64),
   condition        Char(1),
   location         Char(3),
   qty              Dec(5,0));
insert into parts
  values ('m-123',  '1994-01-02', 32),
         ('1234Cf', '2001-08-09',  3),
         ('wf9-2',  '2016-04-21', 14);
insert into locations
  values ('m-123',  'U', 'A02', 2),
         ('1234Cf', 'S', 'A02', 3),
         ('m-123',  'U', 'B01', 1),
         ('wf9-2',  'S', 'A06', 7),
         ('m-123',  'S', 'A18', 29),
         ('wf9-2',  'U', 'F16', 7);

查询:

with -- CTE's
  -- This collects locations into a comma seperated list
  tmp (part_number, condition, location, csv, level) as (
    select part_number, condition, min(location), 
           cast(min(location) as varchar(128)), 1
      from locations
      group by part_number, condition
    union all
    select a.part_number, a.condition, b.location, 
           a.csv || ',' || b.location, a.level + 1
      from tmp a
        join locations b using (part_number, condition)
      where a.csv not like '%' || b.location || '%'
        and b.location > a.location),
  -- This chooses the correct csv list, and adds quantity for the condition
  tmp2 (part_number, condition, csv, qty) as (    
    select t.part_number, t.condition, t.csv, 
           (select sum(qty) qty
              from locations 
              where part_number = t.part_number
                and condition = t.condition)
      from tmp t
      where level = (select max(level)
              from tmp 
              where part_number = t.part_number
                and condition = t.condition))
-- This is the final select that combines the parts file with
-- the second stage CTE and arranges things horizontally by condition
select p.part_number, p.load_date, 
       (select sum(qty) 
          from locations 
          where part_number = p.part_number) as total_qty, 
       coalesce(u.csv, '') as u_loc,
       coalesce(u.qty, 0) as uqty,
       coalesce(s.csv, '') as s_loc, 
       coalesce(s.qty, 0) as sqty
  from parts p
    left outer join tmp2 u
      on u.part_number = p.part_number and u.condition = 'U'
    left outer join tmp2 s
      on s.part_number = p.part_number and s.condition = 'S'
  order by p.load_date;


编辑我不得不在这里添加一些额外的位来支持部件/条件的两个以上位置,并且我已经使CTE中的列命名更加一致。好的,所以让我解释一下,这个quety有3个部分,2个CTE和查询,你可以看到这三个部分是由注释分隔的。第一个CTE是递归CTE。它的目的是产生逗号分隔的位置列表。您应该能够自己运行选择以查看它的功能。 tmp是表名,part_number,condition,csv和level是列名。递归CTE需要SELECT来填充CTE,而UNION ALL需要SELECT来填充下一个细节。在这种情况下,启动SELECT检索该组合的部件号,条件和第一个位置(按字母顺序)。 level设置为1.如果只运行priming select,您将获得:

part_number  condition  location  csv  level
-----------  ---------  --------  ---  -----
1234Cf          S       A01       A02    1
m-123           S       A18       A18    1
m-123           U       A02       A02    1
wf9-2           U       F16       F16    1
wf9-2           S       A06       A06    1

注意每个部件/条件一行。递归CTE的其余部分将填充csv中的其余位置,但它实际上会添加其他记录,因此我们需要在此处及之后过滤结果。因此,记录会在添加时进行处理。上面列出的第一行与位置文件连接在一起 在part_number和条件。请注意,在启动选择中我将第二个min(location)转换为varchar(128)。这为CSV列提供了扩展空间。如果没有这个,它仍然会扩展,但不足以容纳超过2个位置。

递归CTE中的第二个选择将逗号和下一个位置连接到CSV的末尾。执行此操作的具体位是a.csv || ',' || b.location。它还会增加级别列。这有助于我们跟踪查询中的位置。最终,具有最高level的行是我们想要使用的行。我们还有一种方法可以结束递归,还有一些过滤器可以减少添加到临时结果集的行数。如果我们有2个位置,A02B02,请取消选中,我们将获得以下行:A02A02,A02A02,B02,{{1} },A02,A02,A02A02,B02,A02A02,A02,B02,...无限广告。反复制过滤器A02,B02,B02足以让两个位置结束递归,并最小化位置where a.csv not like '%' || b.location || '%'A02的行,如上所述,使用反复制过滤器,我们将获取行B02A02。请注意,将返回第一个具有重复位置的示例的其他结果。使用反复制过滤器添加第三个位置A02,B02将产生以下行:C02A02A02,B02A02,C02,{{1} }。这里没有重复,但我们确实有冗余行,当你添加位置时,它会变得更糟。这是我们需要一种方法来检测这些冗余行。由于我们从最低位置编号开始,因此我们始终可以确保添加到A02,B02,C02的位置大于之前添加的位置。要做到这一点,我们需要做的就是在结果集中包含一个列,指示添加了哪一列(我们可以查询A02,C02,B02,但这更难)。这就是我们需要CSV中的CSV列的原因。然后我们可以编写过滤器location。在上面的3个位置示例中,此过滤器可防止行tmp只留下包含所有三个位置的单行。向位置文件添加三个以上的位置将导致行数在b.location > a.location中扩展得更多,但是对于每个零件和条件,只有一行包含所有位置,并且它将包含所有位置升序。

第二个CTE做了两件事。首先,它过滤A02,C02,B02以删除包含给定部件/条件的所有位置的所有行。其次,它累计每个部分/条件的总量。

执行过滤的位在TMP子句中:

TMP

非常直接。累积零件/条件总量的位也是一个易于理解的子查询:

where

这个怪物查询的最后一块是主要选择。它将零件文件与第二个CTE的结果连接起来,形成最终结果集:

where level = (select max(level)
        from tmp 
        where part_number = t.part_number
          and condition = t.condition))

注意事项是从(select sum(qty) qty from locations where part_number = t.part_number and condition = t.condition) 表中检索总数量的子查询。您可以使用select p.part_number, p.load_date, (select sum(qty) from locations where part_number = p.part_number) as total_qty, coalesce(u.csv, '') as u_loc, coalesce(u.qty, 0) as uqty, coalesce(s.csv, '') as s_loc, coalesce(s.qty, 0) as sqty from parts p left outer join tmp2 u on u.part_number = p.part_number and u.condition = 'U' left outer join tmp2 s on s.part_number = p.part_number and s.condition = 'S' order by p.load_date 中的locations字段,但这可能与tqty表中的实际数量不同步。此外,还有两个左外连接parts,一个用于条件locations,另一个用于条件tmp2。这些构造结果行中的位置/数量的水平数组。最后一件事是U函数。这些给出了空值(当缺少外部连接的结果时)默认值。

编辑结束

最终结果是:

S

注意 coalescepart_number load_date tqty u_loc uqty s_loc sqty ----------- ---------- ---- ------- ---- ----- ---- m-123 1994-01-02 32 A02,B01 3 A18 29 1234Cf 2001-08-09 3 0 A02 3 wf9-2 2016-04-21 14 F16 7 A06 7 在DB2 for i v7.1上可用,而XMLAGG在DB2 for i v7.2中可用。截至2017年8月9日的最新版本是v7.3。正如您使用的是v5r4,您可能不仅需要软件,还需要硬件升级才能获得最新信息。

答案 1 :(得分:0)

不知道UQTY,S_LOC,SQTY的规则是什么,但这里是你问的专栏---

SELECT 
  P.Part_Number,
  P.Load_Date,
  P.TQTY,
  LISTAGG(L.Location, ', ') WITHIN GROUP (ORDER BY L.Location) AS U_LOC    
FROM "Parts Table" AS P
LEFT JOIN "Locations Table" AS L ON P.Part_Number = L.Part_Number
GROUP BY P.Part_Number, P.Load_Date, P.TQTY