T-SQL在表中查找逗号分隔值

时间:2014-08-23 21:28:42

标签: sql sql-server list

零件表

ID名称兼容型号ID(CS值)
- -------- -------------------------
1 Z-Rot 1,2,3
2腐1,2,4
3 Rotil 1,2,7

型号表

ID型号
- -------
10 3.16
9 5.20
7 3.18
1 7.35
2 8.50
3 X5
4 X6


我需要这个结果

ID PART NAME型号型号(ID)
- ---------------- ------------- --------------
1 Z-ROT 7.35,8.50,X5 1,2,3
2 ROT 7.35,8.50,X6 1,2,5
3 ROTIL 7.35,8.50,3.18 1,2,7

我怎么能这样做?

3 个答案:

答案 0 :(得分:1)

虽然最好将数据库表格保持在至少0正常格式(没有重复的组),但如果您坚持使用现有架构,则可以使用Jeff Moden's Tally-Ho CSV splitter来解析CSV字段:

CREATE FUNCTION [dbo].[DelimitedSplit8K]
--===== Define I/O parameters
        (@pString VARCHAR(8000), @pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE!  IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
     -- enough to cover VARCHAR(8000)
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
       ),                          --10E+1 or 10 rows
       E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
       E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
                     -- for both a performance gain and prevention of accidental "overruns"
            SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() 
                                                        OVER (ORDER BY (SELECT NULL)) FROM E4
        ),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just
                     -- once for each delimiter)
            SELECT 1 UNION ALL
            SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
        ),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
            SELECT s.N1,
                   ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
            FROM cteStart s
        )
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final
     -- element when no delimiter is found.
 SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
        Item       = SUBSTRING(@pString, l.N1, l.L1)
   FROM cteLen l
;
go

A usage example is in this dba.stackexchange post of mine

答案 1 :(得分:0)

我强烈建议将表格设计更改为每个兼容型号有一行,最好是单独的表格: CompatibleModelsPerPart表

ID PartId CompatibleModelsId


1 1 1

2 1 2

3 1 3

4 2 1

5 2 2

6 2 4

...

然后必须拆分Models列中的值。之后,您可以执行内部联接,如果数据匹配,您可以旋转结果集(通常在应用程序层)。

答案 2 :(得分:0)

你需要一个分割功能......

分割功能

CREATE FUNCTION [dbo].[split]  
    (  
      @delimited NVARCHAR(MAX),  
      @delimiter NVARCHAR(100)  
    )   
 RETURNS @t TABLE (id INT IDENTITY(1,1), val NVARCHAR(MAX))  
AS  
BEGIN  
  DECLARE @xml XML  
  SET @xml = N'<t>' + REPLACE(@delimited,@delimiter,'</t><t>') + '</t>'  

  INSERT INTO @t(val)  
  SELECT  r.value('.','varchar(MAX)') as item  
  FROM  @xml.nodes('/t') as records(r)  
  RETURN  
END

测试数据

DECLARE @Part TABLE(ID INT, Name VARCHAR(20), CompatibleModels VARCHAR(20))
INSERT INTO @Part VALUES 
(1,'Z-Rot','1,2,3'),
(2,'Rot'  ,'1,2,4'),
(3,'Rotil','1,2,7')


DECLARE @Model TABLE(ID INT, Model VARCHAR(20))
INSERT INTO @Model VALUES 
(10 ,'3.16'),(9  ,'5.20'),(7  ,'3.18'),(1  ,'7.35')
,(2  ,'8.50'),(3  ,'X5'),(4  ,'X6')

查询

 ;WITH CTE AS
 (
  SELECT *
  FROM @Part t
       CROSS APPLY(SELECT Val FROM dbo.split(t.CompatibleModels,','))C(Model_Ids)
),
CTE2 AS
 (
    SELECT C.ID
          ,C.Name AS PartName
          ,C.CompatibleModels
          ,M.Model
          ,Model_Ids
    FROM CTE C INNER JOIN @Model M
    ON C.Model_Ids = M.ID
 ) 
SELECT ID
      ,PartName
      ,STUFF((SELECT ', ' +  Model
              FROM CTE2
              WHERE  C2.ID = ID
              FOR XML PATH(''),TYPE)
              .value('.','NVARCHAR(MAX)'),1,2,'') AS Model
     ,C2.CompatibleModels
FROM CTE2 c2
GROUP BY C2.ID, C2.PartName, C2.CompatibleModels

结果

╔════╦══════════╦══════════════════╦═══════════╗
║ ID ║ PartName ║      Model       ║ Model_Ids ║
╠════╬══════════╬══════════════════╬═══════════╣
║  2 ║ Rot      ║ 7.35, 8.50, X6   ║ 1, 2, 4   ║
║  3 ║ Rotil    ║ 3.18, 7.35, 8.50 ║ 1, 2, 7   ║
║  1 ║ Z-Rot    ║ 7.35, 8.50, X5   ║ 1, 2, 3   ║
╚════╩══════════╩══════════════════╩═══════════╝