2012-08-01 108 views
2

在我的表中,我有一个varchar列,从而存储多值。我的表的一个例子:SQL Server - 使用IN比较Varchar值

RecNum   | Title | Category 
----------------------------------------- 
wja-2012-000001 | abcdef | 4,6 
wja-2012-000002 | qwerty | 1,3,7 
wja-2012-000003 | asdffg | 
wja-2012-000004 | zxcvbb | 2,7 
wja-2012-000005 | ploiuh | 3,4,12 

Category列指向另一个表中的值。

如果我想要检索分类列中值为1,3,5,6,8的行,如何返回相关行?

当我尝试使用IN,我得到'转换失败时转换varchar值'1,3,5,6,8'为数据类型int'错误。

+2

你有数据库的设计和布局的控制。如果是这样,那么最好将ID列添加到此表中,然后添加另一个将类别映射到ID的表。那么你只需要加入表格。 – pstrjds 2012-08-01 04:31:00

+0

大家好,谢谢你给出的所有答案。我无法控制桌子的设计,因此令人头疼。否则,我会进一步标准化表格。 – iceflamez 2012-08-01 09:44:32

回答

2

打破类别到一个单独的表会是一个更好的设计,如果这是你可以做一个改变...否则,您可以创建一个函数值分成整数像这样的表:

CREATE FUNCTION dbo.Split(@String varchar(8000), @Delimiter char(1)) 
returns @temptable TABLE (id int) 
as 
begin 
    declare @idx int 
    declare @slice varchar(8000) 

    select @idx = 1 
     if len(@String)<1 or @String is null return 

    while @idx!= 0 
    begin 
     set @idx = charindex(@Delimiter,@String) 
     if @idx!=0 
      set @slice = left(@String,@idx - 1) 
     else 
      set @slice = @String 

     if(len(@slice)>0) 
      insert into @temptable(id) values(convert(int, @slice)) 

     set @String = right(@String,len(@String) - @idx) 
     if len(@String) = 0 break 
    end 
return 
end 

然后把它从你的查询:

SELECT ... 
FROM ... 
WHERE @SomeID IN (SELECT id FROM dbo.Split(Category, ',')) 

或者,如果你正在寻找提供类作为输入参数(如“1,3,5,6,8”)的列表,并返回表中至少包含其中一个值的所有记录,可以使用如下查询:

SELECT ... 
FROM ... 
WHERE 
    EXISTS (
     select 1 
     from dbo.Split(Category, ',') s1 
     join dbo.Split(@SearchValues, ',') s2 ON s1.id = s2.id 
    ) 
+0

非常感谢您的解决方案:)这很好! – iceflamez 2012-08-01 09:46:04

0

尝试这种解决方案:

CREATE TABLE test4(RecNum varchar(20),Title varchar(10),Category varchar(15)) 
INSERT INTO test4 
VALUES('wja-2012-000001','abcdef','4,6'), 
('wja-2012-000002','qwerty','1,3,7'), 
('wja-2012-000003','asdffg',null), 
('wja-2012-000004','zxcvbb','2,7'), 
('wja-2012-000005','ploiuh','3,4,12') 

select * from test4 
Declare @str varchar(25) = '1,3,5,6,8' 
;WITH CTE as (select RecNum,Title,Category from test4) 
,CTE1 as (
select RecNum,Title,RIGHT(@str,LEN(@str)-CHARINDEX(',',@str,1)) as rem from CTE where category like '%'+LEFT(@str,1)+'%' 
union all 
select c.RecNum,c.Title,RIGHT(c1.rem,LEN(c1.rem)-CHARINDEX(',',c1.rem,1)) as rem from CTE1 c1 inner join CTE c 
on c.category like '%'+LEFT(c1.rem,1)+'%' and CHARINDEX(',',c1.rem,1)>0 
) 
select RecNum,Title from CTE1 
-1

正如其他人所说,你的表的设计违反了基本的数据库设计原则,如果有没有办法解决它,你可以用很少的代码规范表(例如下面),然后加入另一张桌子。在这里你去:

数据:

CREATE TABLE data(RecNum varchar(20),Title varchar(10),Category varchar(15)) 
INSERT INTO data 
VALUES('wja-2012-000001','abcdef','4,6'), 
('wja-2012-000002','qwerty','1,3,7'), 
('wja-2012-000003','asdffg',null), 
('wja-2012-000004','zxcvbb','2,7'), 
('wja-2012-000005','ploiuh','3,4,12') 

这个函数接受一个逗号分隔的字符串,并返回一个表:

CREATE FUNCTION listToTable (@list nvarchar(MAX)) 
    RETURNS @tbl TABLE (number int NOT NULL) AS 
BEGIN 
    DECLARE @pos  int, 
      @nextpos int, 
      @valuelen int 

    SELECT @pos = 0, @nextpos = 1 

    WHILE @nextpos > 0 
    BEGIN 
     SELECT @nextpos = charindex(',', @list, @pos + 1) 
     SELECT @valuelen = CASE WHEN @nextpos > 0 
           THEN @nextpos 
           ELSE len(@list) + 1 
         END - @pos - 1 
     INSERT @tbl (number) 
     VALUES (convert(int, substring(@list, @pos + 1, @valuelen))) 
     SELECT @pos = @nextpos 
    END 
    RETURN 
END 

然后,你可以做这样的事情“正常化”表:

SELECT * 
FROM data m 
CROSS APPLY listToTable(m.Category) AS t 
where Category is not null 

然后使用上述查询的结果与“其他”表加入。例如(我没有测试此查询):

select * from otherTable a 
join listToTable('1,3,5,6,8') b 
on a.Category = b.number 
join(

    SELECT * 
    FROM data m 
    CROSS APPLY listToTable(m.Category) AS t 
    where Category is not null 
    ) c 
on a.category = c.number 
+0

这跟drumboog的 – MakkyNZ 2012-08-01 11:47:11

+0

基本上是一样的答案我同意它是相似的,但不是“一样的”。这个想法是为了演示一种将原始非规范化表转换成可用的方式;这是CROSS APPLY的一部分。然后在JOINS和IN语句中使用查询结果。 IMO更灵活。 – 2012-08-01 16:26:43

1

你可以像这样

声明@var VARCHAR(30); set @ var ='2,3';

EXEC( 'SELECT * FROM类别中,在CATEGORY_ID(' + @ +变种 ')')