SQL - 项目数未知时连接值

SQL - Concatenating values when the number of items is not known

我目前正在刷新我在 SQL 中的知识,但在以下查询中遇到了一些困难。

要求是:

对于每个制造商,以“/”为分隔符按字母顺序列出他生产的所有类型的产品。

推断:制造商、产品类型列表

以下解决方案确实有效,但我不太明白如何..

;with
t1 as 
    (select  maker, type, DENSE_RANK() over(partition by maker  order by type) rn
    from product
),
tr(maker, type,lev) as
    (select distinct t1.maker, cast(t1.type as nvarchar) , 2 from t1 where t1.rn = 1
    union all
    select t1.maker, cast(tr.type +'/'+t1.type as nvarchar), lev + 1
    from t1 join tr on (t1.maker = tr.maker and t1.rn = tr.lev
    )
)

select maker, max(type) names from tr group by maker

这些输出:

1 | A | Laptop/PC/Printer
2 | B | Laptop/PC
3 | C | Laptop
4 | D | Printer
5 | E | PC/Printer

*第二列是制造商,第三列是动态连接的类型列表。

现在,我对 lev 究竟是如何动态增长的感到有点困惑。我在这里缺少某种循环吗? 为什么以2开头? 为什么没有 "cast" 就不能工作?

如果有人能解释这个查询背后的逻辑,我将不胜感激。

非常感谢!

您看到的是一个递归 CTE,一个调用自身的 CTE。就是你认为的 "looping" 效果。当我第一次看到递归时,递归让我产生了一种纠结。查看一些示例并尝试创建一些您自己的简单示例会有所帮助。我仍然不是最擅长的,但我正在变得更好。我在您的代码中添加了一些注释。希望这有帮助。

;WITH t1
AS (
    SELECT  maker,
            type,
            --Partition says each maker is a group so restart at 1
                --Order by type is alphabetic
                --DENSE_RANK() means if there are two the of the same item, they get the same number
            DENSE_RANK() OVER (PARTITION BY maker ORDER BY type) rn
    FROM product
    ),
--This is a recursive CTE meaning a CTE that calls itself(what is doing the "looping"
tr (maker,type,lev)
AS (
    --Grab only the distinct items from your ranked table
    SELECT DISTINCT t1.maker,
        cast(t1.type AS NVARCHAR),
        2 --This is the start of your recursive loop
    FROM t1
    WHERE t1.rn = 1

    UNION ALL

    --Recursively loop through you data, adding each string at the end with the '/'
    SELECT t1.maker,
        cast(tr.type + '/' + t1.type AS NVARCHAR),
        --Plus one to grab the next value
        lev + 1
    FROM t1
    INNER JOIN tr ON (
            --Only match the same makers
            t1.maker = tr.maker
            --Match to the next value
            AND t1.rn = tr.lev
            )
    )

--I think you know what this does
SELECT maker,
    max(type) names
FROM tr
GROUP BY maker