简体   繁体   English

如何在存储过程的IN过滤器内将nvarchar转换为int

[英]How to convert nvarchar to int inside an IN filter on a stored procedure

I have a stored procedure to generate a text file, which one has a lot of filters, here it is 我有一个存储过程来生成一个文本文件,其中有一个有很多过滤器,就在这里

-- =============================================
-- Author:      Ricardo Ríos
-- Create date: 17/01/2014
-- Description: Genera el TXT para Importar a Saint
-- =============================================
ALTER PROCEDURE [dbo].[SP_SAINT_TXT]
    -- Add the parameters for the stored procedure here
        (   
        @nomina VARCHAR(MAX),
        @gerencia VARCHAR(MAX),
        @sucursal VARCHAR(MAX),
        @empresa VARCHAR(MAX),
        @departamento VARCHAR(MAX),
        @cargo VARCHAR(MAX),
        @horario VARCHAR(MAX),
        @locacion VARCHAR(MAX),
        @empleados VARCHAR(MAX),
        @desde DATETIME,
        @hasta DATETIME
        )
AS
BEGIN

SET NOCOUNT ON;

DECLARE @cedula varchar(max), @exnocturnas DECIMAL(5,2),
@diast DECIMAL(5,2), @diasf DECIMAL(5,0), @diasd DECIMAL(5,2),
@matut DECIMAL(5,2), @vespe DECIMAL(5,2), @noctu DECIMAL(5,2),
@linea varchar(max), @txt varchar(max),  
@l1 varchar(max),
@l2 varchar(max),
@l3 varchar(max),
@l4 varchar(max),
@l5 varchar(max),
@l6 varchar(max),
@l7 varchar(max)
SET @txt = ''

SET @nomina = (SELECT REPLACE(@nomina, '(', ''))
SET @nomina = (SELECT REPLACE(@nomina, ')', ''))
SET @gerencia = (SELECT REPLACE(@gerencia, '(', ''))
SET @gerencia = (SELECT REPLACE(@gerencia, ')', ''))
SET @sucursal = (SELECT REPLACE(@sucursal, '(', ''))
SET @sucursal = (SELECT REPLACE(@sucursal, ')', ''))
SET @empresa = (SELECT REPLACE(@empresa, '(', ''))
SET @empresa = (SELECT REPLACE(@empresa, ')', ''))
SET @departamento = (SELECT REPLACE(@departamento, '(', ''))
SET @departamento = (SELECT REPLACE(@departamento, ')', ''))
SET @cargo = (SELECT REPLACE(@cargo, '(', ''))
SET @cargo = (SELECT REPLACE(@cargo, ')', ''))
SET @locacion = (SELECT REPLACE(@locacion,'(',''))
SET @locacion = (SELECT REPLACE(@locacion,')',''))
SET @empleados = (SELECT REPLACE(@empleados,'(',''))
SET @empleados = (SELECT REPLACE(@empleados,')',''))

    declare cursor_txt cursor for
    SELECT B.ID AS cedula, 
    SUM(A.extrasnocturnas) AS extrasnocturnas, 
    SUM(A.diastrabajados) AS diastrabajados, 
    SUM(A.diasfaltantes) AS diasfaltantes, 
    SUM(A.diasdescanso) AS diasdescanso, 
    SUM(A.maturinas) AS maturinas, 
    SUM(A.vespertinas) AS vespertinas, 
    SUM(A.nocturnas) AS nocturnas
    FROM exsaint A
    RIGHT JOIN tabela B ON A.cedula = B.ID
    WHERE A.desde >= @desde AND A.hasta <= @hasta
    AND B.tipo_nomina IN (CASE WHEN @nomina = '-1' THEN B.tipo_nomina ELSE @nomina END)
    AND B.gerencia IN (CASE WHEN @gerencia = '-1' THEN B.gerencia ELSE @gerencia END)
    AND B.sucursal IN (CASE WHEN @sucursal = '-1' THEN B.sucursal ELSE @sucursal END)
    AND B.empresa IN (CASE WHEN @empresa = '-1' THEN B.empresa ELSE @empresa END)
    AND B.departamento IN (CASE WHEN @departamento = '-1' THEN B.departamento ELSE @departamento END)
    AND B.cargo IN (CASE WHEN @cargo = '-1' THEN B.cargo ELSE @cargo END)
    AND B.locacion IN (CASE WHEN @locacion = '-1' THEN B.locacion ELSE @locacion END)
    AND B.ID IN (CASE WHEN @empleados = '-1'THEN B.ID ELSE @empleados END)
    GROUP BY ID
    ORDER BY ID
    open cursor_txt
        fetch next from cursor_txt into @cedula, @exnocturnas, @diast, @diasf, @diasd, @matut, @vespe, @noctu

        while @@fetch_status = 0
        begin
            SET @linea = ''
            SET @l1 = CAST(@exnocturnas AS CHAR(8))
            SET @l2 = CAST(@diast AS CHAR(8))
            SET @l3 = CAST(@diasf AS CHAR(3))
            SET @l4 = CAST(@diasd AS CHAR(8))
            SET @l5 = CAST(@matut AS CHAR(8))
            SET @l6 = CAST(@vespe AS CHAR(8))
            SET @l7 = CAST(@noctu AS CHAR(8))
            SET @linea = 
                LTRIM(CONVERT(CHAR(16),@cedula)) +  
                LTRIM(RTRIM((SELECT CONVERT(VARCHAR(10), @hasta, 103) AS [DD/MM/YYYY]))) + 
                LTRIM(RTRIM('000')) + 
                LTRIM(RTRIM(REPLICATE('0', 8-LEN(@l1)) + @l1 )) +
                LTRIM(RTRIM(REPLICATE('0', 8-LEN(@l2)) + @l2 )) +
                LTRIM(RTRIM(REPLICATE('0', 3-LEN(@l3)) + @l3 )) + 
                LTRIM(RTRIM(REPLICATE('0', 8-LEN(@l4)) + @l4 )) +
                LTRIM(RTRIM(REPLICATE('0', 8-LEN(@l5)) + @l5 )) +
                LTRIM(RTRIM(REPLICATE('0', 8-LEN(@l6)) + @l6 )) +
                LTRIM(RTRIM(REPLICATE('0', 8-LEN(@l7)) + @l7 )) +
                LTRIM(RTRIM('00000000')) +
                + CHAR(13) + CHAR(10);
            PRINT @txt
            PRINT @linea
            SET @txt = @txt + @linea
            fetch next from cursor_txt into @cedula, @exnocturnas, @diast, @diasf, @diasd, @matut, @vespe, @noctu
        end
    close cursor_txt
    deallocate cursor_txt

    SELECT @txt

END

The issue is when I pass some values to the IN filter I get this error. 问题是当我将一些值传递给IN过滤器时,我得到了这个错误。

[Microsoft][ODBC SQL Server Driver][SQL Server]Conversion failed when converting the varchar value ' 5 , 4 ' to data type int. [Microsoft] [ODBC SQL Server驱动程序] [SQL Server]将varchar值“5,4”转换为数据类型int时转换失败。

When I execute the stored procedure like below. 当我执行如下所示的存储过程时。

EXECUTE SP_SAINT_TXT '-1', '-1', '-1', '( 5 , 4 )', '-1', '-1', '-1', '-1', '-1', '20140801', '20150802'   

Is there a way that I can add those filters with some conversions or something else and it works? 有没有办法可以添加一些转换或其他东西的过滤器,它有效吗?

CASE inside IN statements for this task seems not possible because CASE only capable of returning scalar value, according to this post . 根据这篇文章 ,因为CASE只能返回标量值, 因此这个任务的IN语句中的CASE似乎是不可能 Also, SQL Server can't naturally deal with comma-separated value. 此外,SQL Server无法自然地处理逗号分隔值。 If you can afford to change the parameter to a more appropriate data type like table parameter, that would be the ultimate solution. 如果您能够将参数更改为更合适的数据类型(如表参数),那么这将是最终的解决方案。

Otherwise, there are some possible workaround that you can attempt to implement for this task here . 否则,您可以尝试在此处为此任务实施一些可能的解决方法。 This one based on the answer by @CeejeeB . 这个基于@CeejeeB的回答 Change this part of your SQL.. : 更改SQL的这一部分..:

AND B.empresa IN (CASE WHEN @empresa = '-1' THEN B.empresa ELSE @empresa END)

..to this : ..到这个:

AND (
     @empresa = '-1' 
        or 
     B.empresa IN (SELECT t.id.value('.', 'int') id FROM @empresaXml.nodes('//s') as t(id))
    )

The above statement always evaluates to True when @empresa = '-1' . @empresa = '-1'时,上述语句总是计算为True Otherwise, int values will be extracted from @empresaXml variable and will be used to filter column empresa . 否则,将从@empresaXml变量中提取int值,并将其用于过滤列empresa

The variable @empresaXml it self declared and populated as follow : 变量@empresaXml它自我声明并填充如下:

DECLARE @empresaXml XML
SET @empresaXml = CAST('<s>' + REPLACE(@empresa, ',', '</s><s>') + '</s>' AS XML)

SQL Fiddle Demo : SQL小提琴演示

CREATE TABLE MyTable(empresa int, data varchar(10))
;

INSERT INTO MyTable
VALUES
  (1,'a'),
  (2,'b'),
  (3,'c'),
  (4,'d'),
  (5,'e')
;

Declare @empresa varchar(50) = '( 5 , 4 )'
DECLARE @empresaXml XML

SET @empresa = REPLACE(REPLACE(@empresa,'(',''), ')', '')
SET @empresaXml = CAST('<s>' + REPLACE(@empresa, ',', '</s><s>') + '</s>' AS XML)

SELECT *
FROM MyTable B
WHERE (@empresa = '-1' or B.empresa IN (SELECT t.id.value('.', 'int') id
                          FROM @empresaXml.nodes('//s') as t(id)))

output : 输出:

| empresa | data |
|---------|------|
|       4 |    d |
|       5 |    e |

Ah, the issue is that its treating it like a varchar, not an integer. 啊,问题在于它将它视为varchar,而不是整数。

What you need to do is split them out, either into another table or XML. 您需要做的是将它们拆分为另一个表或XML。 Best explained here: 最好解释一下:

Passing a varchar full of comma delimited values to a SQL Server IN function 将一个充满逗号分隔值的varchar传递给SQL Server IN函数

EDIT: Link only answers are bad, so here is the method I use (from that link): 编辑:链接只有答案是坏的,所以这是我使用的方法(从该链接):

Declare @Ids varchar(50)
Set @Ids = ‘1,2,3,5,4,6,7,98,234’

DECLARE @XML XML
SET @XML = CAST('<i>' + REPLACE(@Ids, ',', '</i><i>') + '</i>' AS XML)

SELECT * 
FROM
    SomeTable 
INNER JOIN @XML.nodes('i') x(i) 
    ON  SomeTable .Id = x.i.value('.', 'VARCHAR(MAX)')

I was able to complete this using a Split function. 我能够使用Split功能完成此操作。

I use this DelimitedSplit8k function that Jeff Moden posted on SQLServerCentral in 2012 here: http://www.sqlservercentral.com/articles/Tally+Table/72993/ 我使用了2012年Jeff Moden在SQLServerCentral上发布的DelimitedSplit8k函数: http//www.sqlservercentral.com/articles/Tally+Table/72993/

You can add the Function to your database (3rd to last code box in the article) and then call it like this: 您可以将函数添加到数据库(文章中的倒数第三个代码框),然后像这样调用它:

 AND B.empresa IN (CASE WHEN @empresa = '-1' THEN B.empresa ELSE (SELECT split.item from DelimitedSplit8k(@empresa,',') split) END)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM