2

在我的表中,我有一个 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指向另一个表。

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

当我尝试使用 IN 时,出现“将 varchar 值“1,3,5,6,8”转换为数据类型 int 时出现“转换失败”错误。

4

4 回答 4

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
    )
于 2012-08-01T04:37:22.833 回答
1

你可以这样做

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

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

于 2012-08-01T07:52:06.937 回答
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
于 2012-08-01T05:15:21.977 回答
-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
于 2012-08-01T06:17:55.880 回答