我有这个非结构化表,其中(1)itemID有多个与之关联的receptCodes。我需要做的是在一行中显示与不同orderID相关联的每个receptCode。当前的表结构与此类似:
itemID | receptString
=============================
itemID1 | receptString1
itemID1 | receptString2
itemID1 | receptString3
itemID1 | receptString4
itemID2 | receptString5
itemID2 | receptString6
期望的输出:
itemID | receptString | receptString | receptString | receptString |
============================================================================
itemID1 | receptString1 | receptString2 | receptString3 | receptString4 |
itemID2 | receptString5 | receptString6 |
为此编写查询的正确方法是什么?我尝试使用多个CASE语句和GROUP BY,但我正在努力解决这个问题。每个orderID只能有(5)个MAXS(最大)个接收串,但可以在1-5个接收串之间变化。 receptStrings可以是字符,符号和数字的组合。如果出现NULL,我不担心。这只是我需要的一部分,但却是我正在努力的方向。我在PostgreSQL和Oracle SQL中都对它进行了测试。
*更新*
谢谢大家的建议。问题最终比我们原先预期的要重要得多(我提供的示例表是一小块非常大的馅饼)所以我们决定采取不同的方法。再次感谢。
答案 0 :(得分:1)
以11g测试。
我在这里采用临时分隔符~
。如果您认为字符串字符包含此字符,则可以使用|
或chr(10)
或chr(13)
或任何其他分隔符。它会工作正常。
列名也不能与您想要的相同。我附上1,2,3等等。您可以根据需要更改它们。
with tbl1 (itemID , receptString) as(
select 'itemID1','receptString1' from dual union all
select 'itemID1','receptString2' from dual union all
select 'itemID1','receptString3' from dual union all
select 'itemID1','receptString4' from dual union all
select 'itemID2','receptString5' from dual union all
select 'itemID2','receptString6' from dual
), tbl2 as(
select itemid,listagg(receptString,'~') within group (order by itemid) || '~' as concstr
from tbl1
group by itemid
)
SELECT tbl2.itemid,REGEXP_SUBSTR(concstr ,'([^~]*)(\~)', 1, 1, NULL, 1 ) as receptString1,
REGEXP_SUBSTR(concstr ,'([^~]*)(\~)', 1, 2, NULL, 1 ) as receptString2,
REGEXP_SUBSTR(concstr ,'([^~]*)(\~)', 1, 3, NULL, 1 ) as receptString3,
REGEXP_SUBSTR(concstr ,'([^~]*)(\~)', 1, 4, NULL, 1 ) as receptString4,
REGEXP_SUBSTR(concstr ,'([^~]*)(\~)', 1, 5, NULL, 1 ) as receptString5
from tbl2
首先,我使用listagg连接列并添加了分隔符。然后我使用regep_substr将它们分成列。对于不存在的列,根据此输出,它们将为null。
输出
ITEMID RECEPTSTRING1 RECEPTSTRING2 RECEPTSTRING3 RECEPTSTRING4 RECEPTSTRING5
itemID1 receptString1 receptString2 receptString3 receptString4
itemID2 receptString5 receptString6
答案 1 :(得分:0)
在 Oracle 11g R2 及更高版本上,您可以使用 LISTAGG 进行字符串聚合。
SELECT itemID ,
LISTAGG(receptString, ' | ') WITHIN GROUP (
ORDER BY receptString) AS receptString
FROM table_name
GROUP BY itemID
ORDER BY itemID;
有关不同数据库版本的其他方法,请查看Oracle String Aggregation Techniques。
请记住,这是字符串聚合,而不是 PIVOT 。您将把行聚合到单个分隔的字符串行。
答案 2 :(得分:0)
<强>的PostgreSQL 强>
考虑以下为例
create table ft (itemid text,receptstring text);
insert into ft values
('itemID1','receptString1'),
('itemID1','receptString2'),
('itemID1','receptString3'),
('itemID1','receptString4'),
('itemID2','receptString5'),
('itemID2','receptString6');
并使用WITH
Queries (CTE),string_agg()
,regexp_split_to_array()
WITH CTE
AS (
SELECT itemid || ',' || string_agg(receptstring, ',' ORDER BY receptstring) col
FROM ft
GROUP BY itemid
ORDER BY itemid
)
,cte1
AS (
SELECT regexp_split_to_array(col, ',') AS a
FROM cte
)
SELECT a [1] AS itemid
,a [2] receptString
,a [3] receptString
,a [4] receptString
,a [5] receptString -- you can add more if your condition exceed 1-5
-- limit(ex.a [6] receptString )
FROM cte1
输出:
itemid receptstring receptstring receptstring receptstring receptstring
------- ------------- --------------- --------------- --------------- ---------------
itemID1 receptString1 receptString2 receptString3 receptString4 NULL
itemID2 receptString5 receptString6 NULL NULL NULL
另一种方式
Maximum Columns per Table 250 - 1600 depending on column types,如果某个itemID
有超过1600或250个receptString
的数字?所以我建议你做类似下面的事情
在PostgreSQL中使用string_agg()
select "itemID"
,string_agg("receptString",',' ORDER BY "receptString") receptString
from tbl
group by "itemID"
只有两列,第二列会存储特定receptString
的所有itemID