我有两张桌子:MyOrders和MyDrivers。
在MyOrders表中,我有一个名为Details的列(数据类型TEXT - 我知道,但我没有构建数据库......)
在MyOrders.Details中,有时会出现以逗号分隔的数值列表,这些列表对应于表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中学到很多东西。任何有用的提示/想法将不胜感激。
非常感谢你!
答案 0 :(得分:2)
您可以像这样使用 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中his answer提供的小提琴,我已经测试了他建议的解决方案(拆分字符串)与我建议的解决方案(如)的性能。似乎使用like对于样本数据要快得多(不到一半的时间)(如果数据不同,可能会有不同的结果)。你可以在this link中自行检查。
如果可能的话,我会强烈建议更改数据库结构并创建另一个表来保存当前存储在Details列中的值。
答案 1 :(得分:2)
首先,您需要一个拆分器功能来拆分逗号分隔值。以下是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
<强>结果
| MyOrderID | DriverID | MyDriverID | DriverName |
|-----------|----------|------------|------------|
| 1 | 1 | 1 | Alex |
| 1 | 3 | 3 | Carl |
| 1 | 5 | 5 | Ed |
| 1 | 7 | 7 | George |
| 1 | 9 | 9 | Ichabod |