如何将列表传递给存储过程

时间:2018-08-29 00:34:00

标签: sql-server

我有以下按预期运行的存储过程:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[view_proc]
    (@startdatetime DATETIME,
     @enddatetime DATETIME,
     @ids VARCHAR(MAX)
    )
AS
BEGIN
    SELECT *
    FROM
        (SELECT 
             [sr_datetime], [sr_id], [sr_value], [sr_dst], [sr_source]
         FROM 
             [Powerlink].[dbo].[scada_data]
         WHERE  
             sr_id IN ('M001007', 'M001008', 'M001020', 'M001021')
             AND sr_datetime >= @startdatetime
             AND sr_datetime <= @enddatetime) AS SourceTable 
    PIVOT 
        (MAX(sr_value) 
            FOR [sr_id] IN (M001007, M001008, M001020, M001021)
        ) AS PVT
    ORDER BY 
        sr_datetime
END

我使用以下行运行此过程:

EXEC [dbo].[view_proc] 
           @startdatetime = '2018-01-01 01:00:00', 
           @enddatetime = '2018-01-01 02:00:00', 
           @ids = 'M001007,M001008,M001020,M001021'   

但是,当前该过程不使用@ids@startdatetime参数更改查询的方式来使用@enddatetime参数更改查询。

我的问题是,修改存储过程以使其接受标记列表作为参数的最简单方法是什么,它将用于修改现有查询的结果。

我已经研究过定义一个自定义类型以用作参数,但是我无法弄清楚在调用过程时必须使用哪种语法来表示数据。

任何帮助将不胜感激。

欢呼

2 个答案:

答案 0 :(得分:0)

最好的方法是使用Table-Valued Parameters
您的情况应该是

CREATE TYPE list_of_ids AS TABLE   -- probably should choose a better name
( id VARCHAR(50) );  
GO 
ALTER PROCEDURE [dbo].[view_proc]
    (
     @startdatetime datetime,
     @enddatetime datetime,
     @ids list_of_ids READONLY 
    )

请记住,@ ids是只读的,但是您可以像常规表一样使用它。
如果您有很多ids(10000+),建议您先尝试将其插入到临时表中,可能将其插入索引中,然后在联接或WHERE中使用它。

答案 1 :(得分:0)

一个正在工作的家伙给了我以下函数,该函数采用一串用逗号分隔的值并返回一个表:

USE [Powerlink]
GO
/****** Object:  UserDefinedFunction [dbo].[Split]    Script Date: 30/08/2018 1:49:26         PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[Split] (@sep char(1), @s varchar(512))
RETURNS table
AS
RETURN (
WITH Pieces(pn, start, stop) AS (
  SELECT 1, 1, CHARINDEX(@sep, @s)
  UNION ALL
  SELECT pn + 1, stop + 1, CHARINDEX(@sep, @s, stop + 1)
  FROM Pieces
  WHERE stop > 0
)
SELECT pn,
  SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s
FROM Pieces
)

这使我可以使用以下代码调用存储过程:

EXEC [dbo].[view_proc] 
    @startdatetime = '2018-01-02 00:00:00',
    @enddatetime = '2018-01-31 23:59:59',
    @ids = 'M001007,M001008,M001020,M001021'

这是存储过程中的用法:

USE [Powerlink]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[view_proc]
    (
    @startdatetime varchar(32)= '2018-01-01 01:00:00',
    @enddatetime varchar(32)= '2018-01-01 02:00:00',
    @ids varchar (max) = 'M001007,M001008,M001020,M001021'
    )
    AS

BEGIN
DECLARE @cols AS NVARCHAR(MAX),
@query  AS NVARCHAR(MAX)

SELECT s AS ID INTO #IDS FROM dbo.Split(',',@ids)

select @cols = STUFF((SELECT ',' + QUOTENAME(ID) 
                FROM #IDS
        FOR XML PATH(''), TYPE
        ).value('.', 'NVARCHAR(MAX)') 
    ,1,1,'')

set @query = 
'SELECT *
FROM
(
    SELECT [sr_datetime]
        ,[sr_id]
        ,[sr_value]
        ,[sr_dst]
        ,[sr_source]
    FROM [Powerlink].[dbo].[scada_data]
    WHERE  sr_id IN (SELECT * FROM #IDS)
    AND sr_datetime >= '''+ @startdatetime +'''
    AND sr_datetime <= '''+ @enddatetime +'''
 ) AS SourceTable 
 PIVOT(
        max(sr_value)
        for [sr_id] in (' + @cols + N')
 ) as PVT ORDER BY sr_datetime'

exec sp_executesql @query;
DROP TABLE #IDS
END