Sql server 从逗号分隔列表中的一个表中查找主键

Sql server 从逗号分隔列表中的一个表中查找主键,sql-server,Sql Server,我的任务是根据设计非常糟糕的表结构创建报告 考虑以下两个表。它们包含了每个人都喜欢在每个健身房表演的技巧。请记住,唯一的人员可能会出现在人员表的多行上: PERSONNEL +-----+-----+-------+--------+-----------+ | ID | PID | Name | Gym | Technique | +-----+-----+-------+--------+-----------+ | 1 | 122 | Bob | GymA | 2,3

我的任务是根据设计非常糟糕的表结构创建报告

考虑以下两个表。它们包含了每个人都喜欢在每个健身房表演的技巧。请记住,唯一的人员可能会出现在人员表的多行上:

PERSONNEL
+-----+-----+-------+--------+-----------+
| ID  | PID | Name  | Gym    | Technique |
+-----+-----+-------+--------+-----------+
| 1   | 122 | Bob   | GymA   | 2,3,4     |
+-----+-----+-------+--------+-----------+
| 2   | 131 | Mary  | GymA   | 1,2,4     |
+-----+-----+-------+--------+-----------+
| 3   | 122 | Bob   | GymB   | 1,2,3     |
+-----+-----+-------+--------+-----------+

TECHNIQUES
+-----+------------+
| ID  | Technique  |
+-----+------------+
| 1   | Running    |
+-----+------------+
| 2   | Walking    |
+-----+------------+
| 3   | Hopping    |
+-----+------------+
| 4   | Skipping   |
+-----+------------+
我遇到的问题是,MSSQL查询将可靠地为我提供表中执行某种技术的每个人的列表

例如,假设我想要一个每个喜欢跳绳的人的列表。预期的结果将是:

PREFERS_SKIPPING
+-----+-------+--------+
| PID | Name  | Gym    |
+-----+-------+--------+
| 122 | Bob   | GymA   |
+-----+-------+--------+
| 131 | Mary  | GymA   |
+-----+-------+--------+
同样地:

PREFERS_HOPPING
+-----+-------+--------+
| PID | Name  | Gym    |
+-----+-------+--------+
| 122 | Bob   | GymA   |
+-----+-------+--------+
| 122 | Bob   | GymB   |
+-----+-------+--------+
我可以在ColdFusion中轻松地断开字符串,但由于人员表的大小,这不是一个选项。有人能帮忙吗?

使用此功能

Create FUNCTION F_SplitAsIntTable 
(
@txt varchar(max)
)
RETURNS 
@tab TABLE 
(
 ID int
)
AS
BEGIN
    declare @i int
    declare @s varchar(20)
    Set @i = CHARINDEX(',',@txt)
    While @i>1
        begin
          set @s = LEFT(@txt,@i-1)
          insert into @tab (id) values (@s)
          Set @txt=RIGHT(@txt,Len(@txt)-@i)
          Set @i = CHARINDEX(',',@txt)
        end
    insert into @tab (id) values (@txt) 
    RETURN 
END
您可以这样查询

declare  @a Table  (id int,Name varchar(10),Kind Varchar(100))
insert into @a values (1,'test','1,2,3,4'),(2,'test2','1,2,3,5'),(3,'test3','3,5')

Select a.ID,Name
from @a a
cross apply F_SplitAsIntTable(a.Kind) b 
where b.ID=2
使用此函数

Create FUNCTION F_SplitAsIntTable 
(
@txt varchar(max)
)
RETURNS 
@tab TABLE 
(
 ID int
)
AS
BEGIN
    declare @i int
    declare @s varchar(20)
    Set @i = CHARINDEX(',',@txt)
    While @i>1
        begin
          set @s = LEFT(@txt,@i-1)
          insert into @tab (id) values (@s)
          Set @txt=RIGHT(@txt,Len(@txt)-@i)
          Set @i = CHARINDEX(',',@txt)
        end
    insert into @tab (id) values (@txt) 
    RETURN 
END
您可以这样查询

declare  @a Table  (id int,Name varchar(10),Kind Varchar(100))
insert into @a values (1,'test','1,2,3,4'),(2,'test2','1,2,3,5'),(3,'test3','3,5')

Select a.ID,Name
from @a a
cross apply F_SplitAsIntTable(a.Kind) b 
where b.ID=2

我认为这个查询看起来更清晰:

SELECT p.*, 
t.Technique as ParsedTechnique
FROM Personnel p
JOIN Techniques t
ON CHARINDEX((','+CAST(t.id as varchar(10))+','), (','+p.technique+',')) > 0
WHERE t.id ='1';
您只需将
WHERE t.id=
更改为所需的
TechniqueId


我认为这个查询看起来更清晰:

SELECT p.*, 
t.Technique as ParsedTechnique
FROM Personnel p
JOIN Techniques t
ON CHARINDEX((','+CAST(t.id as varchar(10))+','), (','+p.technique+',')) > 0
WHERE t.id ='1';
您只需将
WHERE t.id=
更改为所需的
TechniqueId


您必须防止的问题之一是阻止“1”匹配“10”和“11”。为此,您需要确保所有值都由分隔符(在本例中为逗号)分隔

下面是一个使用类似于的
的方法,应该可以有效地工作(尽管性能不会那么好):


如果性能有问题,请修复数据结构,包括一个
PersonTechniques
表,以便进行正确的连接。

必须防止的问题之一是阻止“1”匹配“10”和“11”。为此,您需要确保所有值都由分隔符(在本例中为逗号)分隔

下面是一个使用类似于
的方法,应该可以有效地工作(尽管性能不会那么好):


如果性能是一个问题,请修复您的数据结构,包括一个
PersonTechniques
表,以便您可以进行适当的连接。

问题下的第一条注释提供了指向答案的链接。以下是我最终的结果:

WHERE
   p.Technique LIKE '%,29,%' --middle
      OR
   p.Technique LIKE '29,%' --start
      OR
   p.Technique LIKE '%,29' --end
      OR 
   p.Technique =  '29' --single (good point by Cheran S in comment)

乍一看,我认为它不起作用,但巧妙地使用%使它与129等ID不匹配。

问题下的第一条评论提供了答案的链接。以下是我最终的结果:

WHERE
   p.Technique LIKE '%,29,%' --middle
      OR
   p.Technique LIKE '29,%' --start
      OR
   p.Technique LIKE '%,29' --end
      OR 
   p.Technique =  '29' --single (good point by Cheran S in comment)

乍一看,我认为这是行不通的,但巧妙地使用%使其与129等ID不匹配。

请看一看已被接受的答案,我认为这可能是为您提供一个体面解决方案的起点。这可能很有用,不要在表中存储逗号分隔的数据!我知道不要这样做。。。这是最初安装软件的人员创建的表结构。它在应用程序中根深蒂固,我们不愿意更改它。看起来第一个链接可以很好地工作,虽然乍一看我认为不会。请看一看已被接受的答案,我认为这可能是为您提供一个体面解决方案的起点。也许这会很有用。不要在表中存储逗号分隔的数据!我知道不要这样做。。。这是最初安装软件的人员创建的表结构。它在应用程序中根深蒂固,我们不愿意更改它。看起来第一个链接可以正常工作,虽然乍一看我认为不会。不幸的是,当我将其设置为t.id=1时,使用它会返回t.id=11的匹配项。请参阅我的编辑,您可以通过在
id
技术
列的开头和结尾添加逗号来绕过它。由于一些糟糕的数据库设计,我们一直在使用它,所以这个版本确实有效。这个版本很好用,而且非常干净。我会给你评分。不幸的是,当我将t.id=1设置为t.id=11时,使用它会返回匹配项。请参阅我的编辑,您可以通过在
id
technique
列的开头和结尾添加逗号来绕过这一点。由于一些糟糕的数据库设计,我们一直在使用它,所以这个版本确实有效。这个版本很好用,而且非常干净。我相信你。