T-SQL procedimento armazenado que aceita vários valores de Id
-
09-06-2019 - |
Pergunta
Existe uma graciosa maneira de lidar com a passagem de uma lista de ids como um parâmetro para um procedimento armazenado?
Por exemplo, eu quero departamentos 1, 2, 5, 7, 20 retornado pelo meu procedimento armazenado.No passado, eu passei em uma lista delimitada por vírgulas de ids, como o código abaixo, mas me sinto muito sujo fazê-lo.
O SQL Server 2005 é a minha única limitação aplicável, eu acho.
create procedure getDepartments
@DepartmentIds varchar(max)
as
declare @Sql varchar(max)
select @Sql = 'select [Name] from Department where DepartmentId in (' + @DepartmentIds + ')'
exec(@Sql)
Solução
Erland Sommarskog tem mantido a autorização resposta a esta questão, durante os últimos 16 anos: Matrizes e Listas no SQL Server.
Há pelo menos uma dúzia de maneiras para passar uma matriz ou lista para uma consulta;cada um tem seus próprios prós e contras.
- Parâmetros Com Valor De Tabela.O SQL Server 2008 e mais só, e provavelmente o mais próximo de um universal "melhor" aproximação.
- O Método Iterativo.Passar uma seqüência de caracteres delimitada e laços com ele.
- Utilizando o CLR.SQL Server 2005 e superior .A NET languages, apenas.
- XML.Muito bom para a inserção de várias linhas;pode ser um exagero para Seleciona.
- Tabela de Números.Maior desempenho e a complexidade do que um simples método iterativo.
- Comprimento fixo de Elementos.Comprimento fixo melhora a velocidade sobre a seqüência de caracteres delimitada por
- Função de Números.Variações de Tabela de Números e de comprimento fixo, onde o número gerado em uma função, ao invés de incluir tomadas a partir de uma tabela.
- Expressão De Tabela Comum Recursiva (CTE).SQL Server 2005 e versões superiores, ainda não muito complexas e de maior desempenho do que o método iterativo.
- SQL dinâmico.Pode ser lento e tem implicações de segurança.
- Passar a Lista de Muitos Parâmetros.Tedioso e sujeito a erros, mas simples.
- Realmente Lento Métodos.Métodos que utiliza charindex, patindex ou COMO.
Eu realmente não posso recomendar o suficiente para leia o artigo para saber sobre as diferenças entre todas essas opções.
Outras dicas
Sim, a solução actual é propenso a ataques de injeção de SQL.
A melhor solução que eu encontrei é usar uma função que divide o texto em palavras (há alguns postado aqui, ou você pode usar um presente do meu blog) e, em seguida, aderir à sua mesa.Algo como:
SELECT d.[Name]
FROM Department d
JOIN dbo.SplitWords(@DepartmentIds) w ON w.Value = d.DepartmentId
Você pode usar XML.
E. g.
declare @xmlstring as varchar(100)
set @xmlstring = '<args><arg value="42" /><arg2>-1</arg2></args>'
declare @docid int
exec sp_xml_preparedocument @docid output, @xmlstring
select [id],parentid,nodetype,localname,[text]
from openxml(@docid, '/args', 1)
O comando sp_xml_preparedocument é construído em.
Isso produziria o resultado:
id parentid nodetype localname text
0 NULL 1 args NULL
2 0 1 arg NULL
3 2 2 value NULL
5 3 3 #text 42
4 0 1 arg2 NULL
6 4 3 #text -1
que tem tudo (mais?) do que você precisa.
Um método que você pode querer considerar se você estiver indo para trabalhar com valores muito é gravá-los em uma tabela temporária em primeiro lugar.Então você acabou de se juntar a ela como normal.
Desta forma, você está apenas a análise de uma vez.
É mais fácil utilizar um dos 'Split' UDFs, mas muitas pessoas têm postado exemplos de pessoas, eu pensei em ir para um caminho diferente ;)
Este exemplo cria uma tabela temporária para participar em (#tmpDept) e preenchê-lo com o departamento de identificação do que é passado.Eu estou supondo que você está, separando-os com vírgulas, mas você pode -- claro -- alterá-lo para o que você quiser.
IF OBJECT_ID('tempdb..#tmpDept', 'U') IS NOT NULL
BEGIN
DROP TABLE #tmpDept
END
SET @DepartmentIDs=REPLACE(@DepartmentIDs,' ','')
CREATE TABLE #tmpDept (DeptID INT)
DECLARE @DeptID INT
IF IsNumeric(@DepartmentIDs)=1
BEGIN
SET @DeptID=@DepartmentIDs
INSERT INTO #tmpDept (DeptID) SELECT @DeptID
END
ELSE
BEGIN
WHILE CHARINDEX(',',@DepartmentIDs)>0
BEGIN
SET @DeptID=LEFT(@DepartmentIDs,CHARINDEX(',',@DepartmentIDs)-1)
SET @DepartmentIDs=RIGHT(@DepartmentIDs,LEN(@DepartmentIDs)-CHARINDEX(',',@DepartmentIDs))
INSERT INTO #tmpDept (DeptID) SELECT @DeptID
END
END
Isso permitirá que você para passar um id de departamento, vários identificação com vírgulas entre eles, ou até mesmo vários identificação com vírgulas e espaços entre eles.
Então, se você fez algo como:
SELECT Dept.Name
FROM Departments
JOIN #tmpDept ON Departments.DepartmentID=#tmpDept.DeptID
ORDER BY Dept.Name
Você gostaria de ver os nomes de todos do departamento de IDs de que você passou em...
Novamente, isso pode ser simplificada usando-se uma função para preencher a tabela temporária...Eu, principalmente, o fez sem um só para matar o tédio :-P
-- Kevin Fairchild
Um super XML Método, se você quiser usar um procedimento armazenado e passar a lista separada por vírgula do Departamento de IDs :
Declare @XMLList xml
SET @XMLList=cast('<i>'+replace(@DepartmentIDs,',','</i><i>')+'</i>' as xml)
SELECT x.i.value('.','varchar(5)') from @XMLList.nodes('i') x(i))
Todo o crédito vai para o Guru Brad Schulz Blog
Tente Este:
@list_of_params varchar(20) -- value 1, 2, 5, 7, 20
SELECT d.[Name]
FROM Department d
where @list_of_params like ('%'+ CONVERT(VARCHAR(10),d.Id) +'%')
muito simples.