SQL Server 中的函数拆分导致错误

Posted

技术标签:

【中文标题】SQL Server 中的函数拆分导致错误【英文标题】:Function Split in SQL Server causing error 【发布时间】:2017-05-16 06:23:56 【问题描述】:

我想查询以逗号分隔的值列表。但我得到一个错误:

SELECT
    nCmpID, cCompanyName  
FROM 
    (SELECT * 
     FROM tbl_CompanyMaster  
     WHERE nCmpID IN (SELECT * 
                      FROM dbo.fnsplit((SELECT can_AccessCompanyID 
                                        FROM tbl_UserMenuRelations 
                                        WHERE nUserID = 0 
                                          AND Is_Active = 1 
                                          AND Is_Available = 1), ',') a) 
       AND Is_Active = 1) t 

我的函数 FNSplit:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER FUNCTION [dbo].[fnSplit]
    (@sInputList VARCHAR(8000), -- List of delimited items
     @sDelimiter VARCHAR(8000) = ',' -- delimiter that separates items
    ) 
RETURNS @List TABLE (item VARCHAR(8000))
BEGIN
    DECLARE @sItem VARCHAR(8000)

    WHILE CHARINDEX(@sDelimiter, @sInputList, 0) <> 0
    BEGIN
        SELECT
            @sItem = RTRIM(LTRIM(SUBSTRING(@sInputList, 1, CHARINDEX(@sDelimiter, @sInputList, 0) - 1))),
            @sInputList = RTRIM(LTRIM(SUBSTRING(@sInputList, CHARINDEX(@sDelimiter, @sInputList, 0) + LEN(@sDelimiter), LEN(@sInputList))))

        IF LEN(@sItem) > 0
           INSERT INTO @List 
               SELECT @sItem
        END

        IF LEN(@sInputList) > 0
           INSERT INTO @List 
               SELECT @sInputList -- Put the last item in

        RETURN
END

我收到以下错误:

消息 102,第 15 级,状态 1,第 2 行 '(' 附近的语法不正确。

消息 102,第 15 级,状态 1,第 3 行 ',' 附近的语法不正确。

【问题讨论】:

SQL Server 具有内置 类型,这些类型设计 用于保存多个值和能够(相对)有效地提取这些值的机制。它们是 tablesxml(以及更新的版本,json)。您会注意到“包含 CSV 数据的字符串”不在我的列表中。那么,为什么选择使用 CSV 数据,然后努力构建/使用这些字符串,从而引入障碍? 【参考方案1】:

试试这个

SELECT
    nCmpID, cCompanyName  
FROM 
    (SELECT * 
     FROM tbl_CompanyMaster  
     WHERE nCmpID IN (SELECT * 
                      FROM dbo.fnsplit((SELECT can_AccessCompanyID 
                                        FROM tbl_UserMenuRelations 
                                        WHERE nUserID = 0 
                                          AND Is_Active = 1 
                                          AND Is_Available = 1), ',')) 
       AND Is_Active = 1) t

你的代码中有一个无用的 a)

【讨论】:

【参考方案2】:

回答

如果是动态查询则需要用到

Declare @Query varchar(8000)

Declare @CommaSeparatedList varchar(8000)

-- 使用字符串变量存储分割函数的结果。

Set @CommaSeparatedList =(SELECT * 
                  FROM dbo.fnsplit((SELECT can_AccessCompanyID 
                                    FROM tbl_UserMenuRelations 
                                    WHERE nUserID = 0 
                                      AND Is_Active = 1 
                                      AND Is_Available = 1), ','))

SET @Query='SELECT
nCmpID, cCompanyName  
FROM 
(SELECT * 
 FROM tbl_CompanyMaster  
 WHERE nCmpID IN ('+@CommaSeparatedList+') a) 
   AND Is_Active = 1) t' 

 Exec(@Query)

【讨论】:

@marc_s 请检查。 不,这里不需要动态查询..他的代码语法有错误 无需将Answer放在顶部。您正在一个标有“您的答案”的框中写下回复。它已经通过您将其放在那里“标记”为答案(而不是说,将其写在评论中)。 @Damien_The_Unbeliever :注意到我是 *** 的新手,感谢您的建议【参考方案3】:

您在使用用户定义函数 dbo.fnsplit 时语法错误。

您应该使用游标逐行执行。

声明@name VARCHAR(100)

DECLARE record CURSOR FOR SELECT name FROM emp 打开记录

FETCH NEXT FROM record INTO @name

当@@FETCH_STATUS = 0 开始时 select * from dbo.fnsplit(@name,',') FETCH NEXT FROM record INTO @name 结尾 关闭记录 DEALLOCATE 记录

【讨论】:

【参考方案4】:

如果没有必要,不要重新发明***。

Jeff Moden 的示例 CSV 字符串拆分器表值函数:

create function [dbo].[delimitedsplit8K] (
      @pstring varchar(8000)
    , @pdelimiter char(1)
  )
returns table with schemabinding as
 return
  with e1(N) as (
    select 1 union all select 1 union all select 1 union all 
    select 1 union all select 1 union all select 1 union all 
    select 1 union all select 1 union all select 1 union all select 1
  )
  , e2(N) as (select 1 from e1 a, e1 b)
  , e4(N) as (select 1 from e2 a, e2 b)
  , ctetally(N) as (
    select top (isnull(datalength(@pstring),0)) 
      row_number() over (order by (select null)) from e4
  )
  , ctestart(N1) as (
    select 1 union all
    select t.N+1 from ctetally t where substring(@pstring,t.N,1) = @pdelimiter
  )
  , ctelen(N1,L1) as (
    select s.N1,
      isnull(nullif(charindex(@pdelimiter,@pstring,s.N1),0)-s.N1,8000)
    from ctestart s
  )
 select itemnumber = row_number() over(order by l.N1)
      , item       = substring(@pstring, l.N1, l.L1)
   from ctelen l
;
go

并像这样使用:

select cm.nCmpID, cm.cCompanyName 
from tbl_CompanyMaster cm
where cm.Is_Active = 1 
  and cm.nCmpId in (
    select s.Item
    from tbl_UserMenuRelations umr
      cross apply dbo.delimitedsplit8K(umr.can_AccessCompanyId,',') s
    where umr.nUserId = 0 
      and umr.Is_Active = 1 
      and umr.Is_Available = 1
    )

拆分字符串参考:

Tally OH! An Improved SQL 8K “CSV Splitter” Function - Jeff Moden Splitting Strings : A Follow-Up - Aaron Bertrand Split strings the right way – or the next best way - Aaron Bertrand string_split() in SQL Server 2016 : Follow-Up #1 - Aaron Bertrand Ordinal workaround for **string_split()** - Solomon Rutzky

【讨论】:

以上是关于SQL Server 中的函数拆分导致错误的主要内容,如果未能解决你的问题,请参考以下文章

参数导致 SQL Server 中的表扫描

创建表作为选择导致 SQL Server 中的错误 [重复]

使用函数拆分 db2 表中的数据

SQL Server 中的临时表导致“已经有一个名为”的对象错误

SQL server拆分字段的SQL语句

什么类型的错误导致 MS SQL Server 中的 XACT_STATE 为 1?