SQL 服务器 - 将 Table1 连接到 Table2 ON substring(T1.Field) = T2.ID

SQL Server - Join Table1 to Table2 ON substring(T1.Field) = T2.ID

我有两个 table:MyOrders 和 MyDrivers。

在 table MyOrders 中,我有一个名为 Details 的列(数据类型 TEXT - 我知道,但我没有建立数据库...)

在MyOrders.Details中,有时会有comma-separated个数值列表,对应table个MyDrivers的ID值。

目标是使用这些列表将 MyOrders 加入 MyDrivers。

例如:

CREATE TABLE MyOrders 
(
MyOrderID INT IDENTITY,
Details TEXT -- Wish it were NVarchar, but what can I do...
)
GO

CREATE TABLE MyDrivers
(
MyDriverID INT IDENTITY,
DriverName NVARCHAR(50)
)
GO

INSERT INTO MyOrders (Details) VALUES ('1,3,5,7,9')
INSERT INTO MyOrders (Details) VALUES ('2,4,6,8')
INSERT INTO MyOrders (Details) VALUES ('1,2,3,4')
INSERT INTO MyOrders (Details) VALUES ('4,5,6,7,8')
INSERT INTO MyOrders (Details) VALUES (NULL)
INSERT INTO MyOrders (Details) VALUES ('')
INSERT INTO MyOrders (Details) VALUES ('9')

INSERT INTO MyDrivers (DriverName) VALUES ('Alex')
INSERT INTO MyDrivers (DriverName) VALUES ('Bobby')
INSERT INTO MyDrivers (DriverName) VALUES ('Carl')
INSERT INTO MyDrivers (DriverName) VALUES ('Daryl')
INSERT INTO MyDrivers (DriverName) VALUES ('Ed')
INSERT INTO MyDrivers (DriverName) VALUES ('Frank')
INSERT INTO MyDrivers (DriverName) VALUES ('George')
INSERT INTO MyDrivers (DriverName) VALUES ('Hal')
INSERT INTO MyDrivers (DriverName) VALUES ('Ichabod')
INSERT INTO MyDrivers (DriverName) VALUES ('Justin Timberlake')

SELECT * FROM MyOrders O
INNER JOIN MyDrivers D
    ON D.MyDriverID = ...? substring()? patindex()?
WHERE O.MyOrderID = 1

此处期望的结果是,对于 MyOrderID 1,我将收到 5 行结果:在同一订单的详细信息列表中,分配给该订单的五个司机中的每一个。如果没有列表 (NULL, '', ' ', ' ') 那么我不想返回任何行。 **有时用户会删除此字段中的值并在后面留下空格,所以我假设我必须使用 TRIM。但他们总是添加必要的逗号,所以至少有...

我不知道该怎么做; SQL 我还有很多东西要学。任何有用的 tips/ideas 将不胜感激。

非常感谢您!

你可以这样使用IN

SELECT * 
FROM MyOrders O
INNER JOIN MyDrivers D
ON ',' + CAST(D.MyDriverID as varchar) +',' IN(','+ ISNULL(O.Details, '')  +',')
WHERE O.MyOrderID = 1

更新
实际上,你不能使用IN,但你可以使用LIKE。这样做的原因是 IN 需要一个值列表而不是用逗号分隔的单个字符串值。

SELECT MyOrderID, MyDriverID, DriverName
FROM MyOrders O
INNER JOIN MyDrivers D
ON ','+ cast(ISNULL(O.Details, '') as varchar(max))  +',' LIKE 
   '%,' + CAST(D.MyDriverID as varchar) +',%'
WHERE O.MyOrderID = 1

使用 wewesthemenace in , I've tested his suggested solution (split string) vs my suggested solution (like) for performance. it seems that using like is much faster (less then half of the time) for your sample data (it could be different results if the data is different). you can check it yourself in this link 提供的 fiddle。

如果可能,我会强烈建议更改数据库结构并创建另一个table来保存当前存储在“详细信息”列中的值。

首先,您需要一个拆分器函数来拆分您的逗号分隔值。这是 Jeff Moden 为目前最快的分离器之一编写的 DelmitedSplitN4K 函数。阅读此 article 了解更多信息。

CREATE FUNCTION [dbo].[DelimitedSplitN4K](
    @pString NVARCHAR(4000), 
    @pDelimiter NCHAR(1)
)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN

WITH E1(N) AS (
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
),
E2(N) AS (SELECT 1 FROM E1 a, E1 b),
E4(N) AS (SELECT 1 FROM E2 a, E2 b),
cteTally(N) AS(
    SELECT TOP (ISNULL(DATALENGTH(@pString)/2,0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (
    SELECT 1 UNION ALL 
    SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
),
cteLen(N1,L1) AS(
    SELECT s.N1,
        ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,4000)
    FROM cteStart s
)
SELECT 
    ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
    Item       = SUBSTRING(@pString, l.N1, l.L1)
FROM cteLen l
;

然后,你想先拆分MyOrders.Details中的值。拆分后,您执行 JOIN 以获得所需的结果:

WITH CteSplitted AS(
    SELECT
        mo.MyOrderID,
        CAST(s.Item AS INT) AS DriverID
    FROM MyOrders mo
    CROSS APPLY dbo.DelimitedSplitN4K(CONVERT(NVARCHAR(4000), mo.Details), ',') s
)
SELECT * 
FROM CteSplitted cs
INNER JOIN MyDrivers d
    ON d.MyDriverID = cs.DriverID
WHERE cs.MyOrderID = 1

SQL Fiddle

结果

| MyOrderID | DriverID | MyDriverID | DriverName |
|-----------|----------|------------|------------|
|         1 |        1 |          1 |       Alex |
|         1 |        3 |          3 |       Carl |
|         1 |        5 |          5 |         Ed |
|         1 |        7 |          7 |     George |
|         1 |        9 |          9 |    Ichabod |