You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL Server 2012:如何对同表两分隔列执行CROSS JOIN?

解决SQL Server 2012中CSV列的行内笛卡尔积合并需求

嘿,这个需求我之前帮不少人处理过,SQL Server 2012因为还没有内置的STRING_SPLIT函数(那是2016及以后版本才有的),所以得用点替代方案。下面给你两种可行的方法,都能实现你要的:把每行里Delimited_Col_1和Delimited_Col_2的CSV值做行内笛卡尔积,最后合并所有行的结果。

方法一:用XML拆分(无需创建函数)

这种方法不用额外创建函数,适合临时查询使用。假设你的表叫YourTable,结构包含唯一标识列(比如ID)和两个CSV列,先给你一个带测试数据的完整示例:

测试数据准备

-- 创建测试表
CREATE TABLE YourTable (
    ID INT PRIMARY KEY,
    Delimited_Col_1 VARCHAR(MAX),
    Delimited_Col_2 VARCHAR(MAX)
);

-- 插入测试数据
INSERT INTO YourTable VALUES
(1, 'A,B,C', 'X,Y'),
(2, 'D,E', 'Z'),
(3, 'F', 'G,H,I');

实现查询

WITH SplitCol1 AS (
    SELECT 
        ID,
        -- 去掉拆分后值的前后空格
        LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) AS Val1
    FROM (
        -- 把CSV字符串转成XML格式,用逗号分隔节点
        SELECT 
            ID,
            CAST('<Item>' + REPLACE(Delimited_Col_1, ',', '</Item><Item>') + '</Item>' AS XML) AS XmlData
        FROM YourTable
        -- 过滤空值和空字符串,避免无效拆分
        WHERE Delimited_Col_1 IS NOT NULL AND Delimited_Col_1 != ''
    ) AS RawData
    -- 拆分XML节点为行
    CROSS APPLY XmlData.nodes('/Item') AS Split(a)
),
SplitCol2 AS (
    SELECT 
        ID,
        LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) AS Val2
    FROM (
        SELECT 
            ID,
            CAST('<Item>' + REPLACE(Delimited_Col_2, ',', '</Item><Item>') + '</Item>' AS XML) AS XmlData
        FROM YourTable
        WHERE Delimited_Col_2 IS NOT NULL AND Delimited_Col_2 != ''
    ) AS RawData
    CROSS APPLY XmlData.nodes('/Item') AS Split(a)
)
-- 关联同一行的拆分结果,得到笛卡尔积
SELECT sc1.Val1, sc2.Val2
FROM SplitCol1 sc1
INNER JOIN SplitCol2 sc2 ON sc1.ID = sc2.ID
ORDER BY sc1.ID, sc1.Val1, sc2.Val2;

结果说明

执行后会得到每个CSV列值的组合,比如第一行的A/B/C和X/Y会生成A-X、A-Y、B-X、B-Y、C-X、C-Y,所有行的结果会合并在一起。

方法二:自定义表值拆分函数(复用性更强)

如果需要多次执行这类查询,创建一个通用的拆分函数会更方便。

创建拆分函数

CREATE FUNCTION dbo.SplitCSV (@InputString VARCHAR(MAX), @Delimiter CHAR(1))
RETURNS @OutputTable TABLE (Value VARCHAR(100))
AS
BEGIN
    DECLARE @StartIndex INT = 1;
    DECLARE @EndIndex INT;

    -- 确保字符串末尾有分隔符,避免漏拆最后一个值
    IF RIGHT(@InputString, 1) != @Delimiter
    BEGIN
        SET @InputString = @InputString + @Delimiter;
    END;

    -- 循环拆分字符串
    WHILE CHARINDEX(@Delimiter, @InputString) > 0
    BEGIN
        SET @EndIndex = CHARINDEX(@Delimiter, @InputString);
        -- 插入拆分后的值(去掉前后空格)
        INSERT INTO @OutputTable(Value)
        SELECT LTRIM(RTRIM(SUBSTRING(@InputString, @StartIndex, @EndIndex - @StartIndex)));
        -- 截取剩余字符串继续拆分
        SET @InputString = SUBSTRING(@InputString, @EndIndex + 1, LEN(@InputString));
    END;

    RETURN;
END;

使用函数实现查询

SELECT 
    sc1.Value AS Val1,
    sc2.Value AS Val2
FROM YourTable t
-- 拆分Delimited_Col_1
CROSS APPLY dbo.SplitCSV(t.Delimited_Col_1, ',') sc1
-- 拆分Delimited_Col_2,和上面的拆分结果做行内笛卡尔积
CROSS APPLY dbo.SplitCSV(t.Delimited_Col_2, ',') sc2
-- 过滤空值
WHERE t.Delimited_Col_1 IS NOT NULL AND t.Delimited_Col_1 != ''
  AND t.Delimited_Col_2 IS NOT NULL AND t.Delimited_Col_2 != ''
ORDER BY t.ID, sc1.Value, sc2.Value;

优势说明

这个写法更简洁,CROSS APPLY会自动把每行的CSV列拆分成多行,两个CROSS APPLY组合就直接实现了行内的笛卡尔积,后续再用类似需求直接调用函数即可。

注意事项

  • 如果你的CSV值里包含带逗号的内容(比如"John,Doe"),上述两种方法都会失效,这种情况需要用更复杂的拆分逻辑(比如处理引号),不过如果是普通的无嵌套逗号的CSV,这两种方法完全够用。
  • 3000行数据量不大,两种方法的性能都能满足需求,自定义函数可能比XML方法稍微慢一点,但差异可以忽略。

内容的提问来源于stack exchange,提问作者Sashank Allamraju

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:43:21