SQL Server - 使用IN比较Varchar值

时间:2012-08-01 04:24:33

标签: sql sql-server tsql

在我的表中,我有一个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'错误。

4 个答案:

答案 0 :(得分: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
    )

答案 1 :(得分:1)

你可以这样做

  

声明@var varchar(30);设置@ var ='2,3';

     

exec('select * from Category_Id in('+ @ var +')')

答案 2 :(得分: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

答案 3 :(得分:-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