【问题标题】:How to i get result based on ids list passed as a varchar?如何根据作为 varchar 传递的 ids 列表获取结果?
【发布时间】:2014-01-29 23:06:09
【问题描述】:

我将 ids 列表作为 varchar(500) 传递,并且基于该 ids 记录是必需的。我的 sql 代码是

declare @Ids varchar(500) = '12964,12965,12966'

select *
from tblBooks
where BookID in (@Ids)

其中 BookID 是 varchar(50)。ID 数可以是 100。将 @Ids 转换为 int 会出现以下错误

转换 varchar 值时转换失败 '12964,12965,12966' 转数据类型 int

@Id 未转换为 Int,我如何找到结果。

【问题讨论】:

  • 如果你的id 是一个整数,你为什么要尝试将它们作为varchar 传递?
  • 我正在通过 c# 应用程序。
  • 您当前的计划存在严重缺陷。您确实需要按原样实现它的帮助(因为您无法更改设计),还是希望以正确的方式实现它?
  • OP 询问如何将变量列表作为参数传递给 SQL 语句。他尝试使用逗号分隔值作为第一种方法。这是一个常见且直观的第一次尝试。

标签: c# sql sql-server sql-server-2008


【解决方案1】:

使用表变量:

DECLARE @Ids TABLE (ID INT);
INSERT @Ids VALUES (12964),(12965),(12966);

SELECT *
FROM tblBooks
WHERE BookID in (SELECT ID FROM @Ids);

如果您需要将其传递给过程,则可以使用表值参数:

CREATE TYPE dbo.ListOfInt AS TABLE (ID INT);
GO
CREATE PROCEDURE dbo.GetBooks @IDs dbo.ListOfInt READONLY
AS
BEGIN
    SELECT *
    FROM tblBooks
    WHERE BookID in (SELECT ID FROM @Ids);
END
GO

DECLARE @IDs dbo.ListofInt;
INSERT @Ids VALUES (12964),(12965),(12966);
EXECUTE dbo.GetBooks @Ids;

或来自 c#

var table = new DataTable();
table.Columns.Add("ID", typeof(int));

// ADD YOUR LIST TO THE TABLE

using (var connection = new SqlConnection("Connection String"))
using (var command = new SqlCommand("dbo.GetBooks", connection))
{
    command.CommandType = CommandType.StoredProcedure;
    var param = new SqlParameter("@Ids", SqlDbType.Structured);
    param.TypeName = "dbo.ListofInt";
    param.Value = table;
    command.Parameters.Add(table);
    connection.Open();

    using (var reader = command.ExecuteReader())
    {
        while (reader.Read())
        {
            // do something
        }
    }
}

一旦 TYPE 就位,您甚至不需要使用存储过程。您可以简单地调用普通查询:

using (var connection = new SqlConnection("Connection String"))
using (var command = new SqlCommand("SELECT * FROM tblBooks WHERE BookID IN (SELECT ID FROM @IDs)", connection))
{
    var param = new SqlParameter("@Ids", SqlDbType.Structured);
    param.TypeName = "dbo.ListofInt";
    param.Value = table;
    command.Parameters.Add(table);
    connection.Open();

    // ETC
}

在 c# 中使用 String.Split() 进行拆分并将列表传递给 SQL 将比在 SQL 中进行拆分的任何方法更有效

【讨论】:

  • 这是首选方法。不过,您还没有展示如何从 C# 应用程序调用它。这将是 OP 的下一个问题。
  • @Yuck 是的,我刚刚看到,当你发表评论时,我已经在写编辑了。
【解决方案2】:

您可以这样编写查询:

declare @Ids varchar(500) = '12964,12965,12966'

select *
from tblBooks
where ','+cast(BookID as varchar(500))+',' like '%,'+@Ids+',%';

但您不想这样做,因为性能很差——查询不能使用索引。

其他三个选项。使用动态 SQL 并将列表直接插入查询。或者使用split 函数来拆分字符串。或者使用表变量:

declare @ids table (id int);
insert into @ids(id)
    select 12964 union all select 12965 union all select 12966;

select b.*
from tblBooks b
where b.BookId in (select id from @ids);

【讨论】:

    【解决方案3】:

    这行不通。 SQL Server 不会为您隐式拆分字符串,SQL Server 中也没有内置的字符串拆分功能。

    如果您通过 C# 驱动它,您可以使用表值参数。您还可以通过 Dapper-Dot-Net 传递您的查询,它会自动参数化“In”查询。

    如果你真的必须在 T-SQL 中这样做,你也可以使用字符串拆分逻辑,这里比较简洁。

    SELECT i.value('./text()[1]', 'int') [id] into #ids 
    FROM( values(CONVERT(xml,'<r>' + REPLACE(@Ids+left(@@dbts,0),',','</r><r>') +  '</r>')) ) a(_) 
    CROSS APPLY _.nodes('./r') x(i)
    select *
    from tblBooks a
    join #ids i on i.id = a.bookId
    

    【讨论】:

      【解决方案4】:

      创建这个函数:

      CREATE FUNCTION [dbo].[SplitDelimiterString] (@StringWithDelimiter VARCHAR(8000), @Delimiter VARCHAR(8))
      
      RETURNS @ItemTable TABLE (Item VARCHAR(8000))
      
      AS
      BEGIN
          DECLARE @StartingPosition INT;
          DECLARE @ItemInString VARCHAR(8000);
      
          SELECT @StartingPosition = 1;
          --Return if string is null or empty
          IF LEN(@StringWithDelimiter) = 0 OR @StringWithDelimiter IS NULL RETURN; 
      
          WHILE @StartingPosition > 0
          BEGIN
              --Get starting index of delimiter .. If string
              --doesn't contain any delimiter than it will returl 0 
              SET @StartingPosition = CHARINDEX(@Delimiter,@StringWithDelimiter); 
      
              --Get item from string        
              IF @StartingPosition > 0                
                  SET @ItemInString = SUBSTRING(@StringWithDelimiter,0,@StartingPosition)
              ELSE
                  SET @ItemInString = @StringWithDelimiter;
              --If item isn't empty than add to return table    
              IF( LEN(@ItemInString) > 0)
                  INSERT INTO @ItemTable(Item) VALUES (@ItemInString);            
      
              --Remove inserted item from string
              SET @StringWithDelimiter = SUBSTRING(@StringWithDelimiter,@StartingPosition + 
                           LEN(@Delimiter),LEN(@StringWithDelimiter) - @StartingPosition)
      
              --Break loop if string is empty
              IF LEN(@StringWithDelimiter) = 0 BREAK;
          END
      
          RETURN
      END
      

      然后这样称呼它:

      declare @Ids varchar(500) = '12964,12965,12966'
      
      select *
      from tblBooks
      where BookID in (SELECT * FROM dbo.SplitDelimiterString(@ids,','))
      

      【讨论】:

      • 有趣的解决方案。可能不是世界上最高效的事情,但完成工作值得称赞。
      【解决方案5】:

      一种方法是将 int 转换为 varchar。还有很多其他的方法......

      选择 * 来自 tblBooks 其中 CAST(BookID as varchar(50)) in (@Ids)

      相关:Define variable to use with IN operator (T-SQL)

      【讨论】:

        猜你喜欢
        • 2016-02-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多