【问题标题】:Using Parameters to Add Columns?使用参数添加列?
【发布时间】:2017-05-14 13:10:31
【问题描述】:

我有一个存储过程,可以将货币记录添加到我的汇率表中,但我还需要一个用于添加货币的相应列。

表结构如下:

CurrencyID  CurrencyName Rupee                  Euro                   Dollar                 Pound
----------- ------------ ---------------------- ---------------------- ---------------------- ----------------------
1           Rupee        1                      0.008                  0.009                  0.007
2           Euro         121.3                  1                      1.08                   0.84
3           Dollar       111.4                  0.91                   1                      0.77
4           Pound        143.6                  1.18                   1.28                   1

到目前为止我的存储过程是这样的:

CREATE PROCEDURE addCurrency 
    @CurrencyName varchar(30),
    @Rupee float,
    @Euro float,
    @Dollar float,
    @Pound float
AS
BEGIN
    INSERT into [dbo].[CurrencyTbl] (CurrencyName, Rupee, Euro, Dollar, Pound) 
    VALUES (@CurrencyName, @Rupee, @Euro, @Dollar, @Pound)
END
BEGIN
    DECLARE @SQL VARCHAR(1000)

    SELECT @SQL = 'ALTER TABLE [dbo].[CurrencyTbl] ADD ' + @CurrencyName + ' VARCHAR(30)'
END

但未创建列

【问题讨论】:

    标签: sql-server database stored-procedures dynamic-sql


    【解决方案1】:

    我认为这不是一个好主意,但您实际上并没有执行您创建的@SQL,您只是在选择它。

    您的代码容易受到 SQL 注入的影响(因为您直接执行带有连接参数的 sql),所以要小心该代码。

    另外,如果你存储的是数字,为什么你的数据类型是varchar(30)?您的其他数据类型是 float(应该是 numeric/decimal 而不是 float)。

    你可以像这样使用exec sp_executesql @SQL

    CREATE PROC addCurrency @CurrencyName  varchar(30),@Rupee float,
    @Euro float,@Dollar float,@Pound float
        AS
        BEGIN
        INSERT into [dbo].[CurrencyTbl] (CurrencyName , Rupee,Euro, Dollar,Pound ) 
        VALUES (@CurrencyName,@Rupee,@Euro,@Dollar,@Pound )
        END
        BEGIN
        Declare @SQL nVarChar(1000)
        SELECT @SQL = 'ALTER TABLE [dbo].[CurrencyTbl] ADD ' + @CurrencyName + ' float;'
        exec sp_executesql @SQL;
        END
    

    动态sql


    例如:
    /* Monies is the term used by Irkens to refer to their form of currency */
    exec addCurrency 'Monies',1,1,1,1 
    select * from CurrencyTbl
    

    rextester 演示:http://rextester.com/CUC99912

    返回:

    +------------+--------------+------------+----------+----------+----------+--------+
    | CurrencyID | CurrencyName |   Rupee    |   Euro   |  Dollar  |  Pound   | Monies |
    +------------+--------------+------------+----------+----------+----------+--------+
    |          1 | Rupee        | 1,000000   | 0,008000 | 0,009000 | 0,007000 | NULL   |
    |          2 | Euro         | 121,300000 | 1,000000 | 1,080000 | 0,840000 | NULL   |
    |          3 | Dollar       | 111,400000 | 0,910000 | 1,000000 | 0,770000 | NULL   |
    |          4 | Pound        | 143,600000 | 1,180000 | 1,280000 | 1,000000 | NULL   |
    |          5 | Monies       | 1,000000   | 1,000000 | 1,000000 | 1,000000 | NULL   |
    +------------+--------------+------------+----------+----------+----------+--------+
    

    最好为您的表考虑一种不需要添加新列和使用动态 sql 更新列的替代形式。

    这是一种选择:

    create table CurrencyTbl (FromCurrencyName varchar(30), ExchangeRate decimal(19,6), ToCurrencyName varchar(30))
    insert into CurrencyTbl values
     ('Rupee ',1.000000,'Rupee')
    ,('Rupee ',0.008000,'Euro')
    ,('Rupee ',0.009000,'Dollar')
    ,('Rupee ',0.007000,'Pound')
    ,('Euro  ',121.300000,'Rupee')
    ,('Euro  ',1.000000,'Euro')
    ,('Euro  ',1.090000,'Dollar')
    ,('Euro  ',0.850000,'Pound')
    ,('Dollar',111.400000,'Rupee')
    ,('Dollar',0.910000,'Euro')
    ,('Dollar',1.000000,'Dollar')
    ,('Dollar',0.770000,'Pound')
    ,('Pound ',143.600000,'Rupee')
    ,('Pound ',1.180000,'Euro')
    ,('Pound ',1.280000,'Dollar')
    ,('Pound ',1.000000,'Pound')
    

    您可以像这样动态地旋转表格:

    declare @cols nvarchar(max);
    declare @sql  nvarchar(max);
      select @cols = stuff((
        select distinct 
          ', ' + quotename(ToCurrencyName)
          from CurrencyTbl
          for xml path (''), type).value('.','nvarchar(max)')
        ,1,1,'')
    select  @sql = '
     select FromCurrencyName as CurrencyName,' + @cols + '
      from CurrencyTbl
     pivot (max([ExchangeRate]) for [ToCurrencyName] in (' + @cols + ') ) p'
    exec sp_executesql @sql;
    

    rextester 演示:http://rextester.com/EQSC62833

    返回:

    +--------------+----------+----------+----------+------------+
    | CurrencyName |  Dollar  |   Euro   |  Pound   |   Rupee    |
    +--------------+----------+----------+----------+------------+
    | Dollar       | 1,000000 | 0,910000 | 0,770000 | 111,400000 |
    | Euro         | 1,090000 | 1,000000 | 0,850000 | 121,300000 |
    | Pound        | 1,280000 | 1,180000 | 1,000000 | 143,600000 |
    | Rupee        | 0,009000 | 0,008000 | 0,007000 | 1,000000   |
    +--------------+----------+----------+----------+------------+
    

    【讨论】:

    • @RamblinRose 因为CurrencyName 是表中的一列。
    • @GerMc 可以,但最好将所有这些存储在规范化表中,然后在前端应用程序中进行透视或使用动态透视。
    • @GerMc 你还没有说它应该是什么=,你需要用单引号包裹卢比。我编辑了您的 rextester,刷新以查看更改。
    • @GerMc 为什么在你将另一个答案标记为答案时,你还一直问我问题?
    • @GerMc 根据示例数据中的一些问题,数学并不能完全准确地计算出来,但您应该能够在这里得到这个想法:rextester.com/YRDB40508
    【解决方案2】:

    我创建了临时表,但这也应该适用于标准表...

    CREATE TABLE #Currencies (
    Currencyid INTEGER identity(1, 1) PRIMARY KEY
    ,CurrencyName VARCHAR(255)
    )
    
    CREATE TABLE #ConversionRates (
    CurrencyId1 INTEGER
    ,CurrencyId2 INTEGER
    ,Conversion1To2Rate DECIMAL(19, 6)
    )
    

    然后我们有一个存储过程来处理工作

    /************************************************************************************************
    Stored Procedure: dbo.usp_AddCurrency
    M. Jay Wheeler  -- mjwheele@yahoo.com
    ****************************************************************************************************/
    CREATE PROCEDURE [dbo].usp_AddCurrency --
        @CurrencyName VARCHAR(255) = NULL
        ,@CurrencyId INTEGER = NULL
        ,@Currency2Id INTEGER = NULL
        ,@Currency1ToCurrency2ExchangeRate DECIMAL(19, 6) = NULL
    AS
    BEGIN
        DECLARE @ErrorMessage VARCHAR(MAX) = ''
        DECLARE @ThrowErrorOnAddingAlreadyExists CHAR(1) = 'N' -- If N will automatically update the currency Name.
    
        BEGIN TRY
            --#############################################################################
            -- Check Parameters
            --#############################################################################
            IF @CurrencyId IS NULL
                AND @CurrencyName IS NULL
                SELECT @ErrorMessage += '@CurrencyID and @CurrencyName cannot both be null.' + CHAR(13)
    
            IF @CurrencyId IS NULL
                AND NOT EXISTS (
                    SELECT 1
                    FROM #Currencies
                    WHERE CurrencyName = @CurrencyName
                    )
            BEGIN
                INSERT INTO #Currencies (CurrencyName)
                SELECT @CurrencyName
    
                SELECT @CurrencyId = SCOPE_IDENTITY()
            END
    
            SELECT @CurrencyId = CurrencyId
            FROM #Currencies
            WHERE CurrencyName = @CurrencyName
                AND @CurrencyId IS NULL
    
            SELECT @CurrencyName = CurrencyName
            FROM #Currencies
            WHERE Currencyid = @CurrencyId
                AND @CurrencyName IS NULL
    
            IF NOT EXISTS (
                    SELECT 1
                    FROM #Currencies
                    WHERE Currencyid = @CurrencyId
                    )
                SELECT @ErrorMessage += '@CurrencyID: ' + isnull(Cast(@CurrencyId AS VARCHAR(15)), 'NULL') + ' Does Not Exist.' + CHAR(13)
    
            IF EXISTS (
                    SELECT 1
                    FROM #Currencies c
                    WHERE c.Currencyid <> @CurrencyId
                        AND c.CurrencyName = @CurrencyName
                    )
                SELECT @ErrorMessage += 'Currency: ' + @CurrencyName + ' already exists with an ID of ' + cast((
                            SELECT currencyId
                            FROM #Currencies
                            WHERE CurrencyName = @CurrencyName
                            ) AS VARCHAR(15)) + CHAR(13)
    
            IF EXISTS (
                    SELECT 1
                    FROM #Currencies c
                    WHERE c.Currencyid = @CurrencyId
                        AND c.CurrencyName <> @CurrencyName
                    )
                UPDATE #Currencies
                SET CurrencyName = @CurrencyName
                WHERE Currencyid = @CurrencyId
    
            IF @Currency2Id IS NOT NULL
                AND @Currency1ToCurrency2ExchangeRate IS NULL
                SELECT @Errormessage += 'Improper Syntax no exchange rate to assignmen.' + CHAR(13)
    
            IF @Currency2id IS NULL
                AND @Currency1ToCurrency2ExchangeRate IS NOT NULL
                SELECT @Errormessage += 'Improper Syntax no "To Currency" for rate assignment.' + CHAR(13)
    
            IF len(@ErrorMessage) > 0
             RAISERROR ('%s' ,16 ,1 ,@ErrorMessage)
    
            --#############################################################################
            -- you get the idea more error checking needed to bullet proof.  Now assume
            -- We have all the correct Parameters set up, we've made sure anything coming in
            -- needs to be added or updated by this point.
            --#############################################################################
            --#############################################################################
            -- nothing to do if @Currency2Id is null
            --#############################################################################
            IF @Currency2Id IS NULL
                RETURN
    
            --#############################################################################
            -- see if we need to add/Update a conversion rate.
            --#############################################################################
            UPDATE #ConversionRates
            SET Conversion1To2Rate = @Currency1ToCurrency2ExchangeRate
            WHERE @CurrencyId = CurrencyId1
                AND @Currency2Id = CurrencyId2
    
            INSERT INTO #ConversionRates (
                CurrencyId1
                ,CurrencyId2
                ,Conversion1To2Rate
                )
            SELECT @CurrencyId
                ,@Currency2Id
                ,@Currency1ToCurrency2ExchangeRate
            WHERE NOT EXISTS (
                    SELECT 1
                    FROM #ConversionRates
                    WHERE @CurrencyId = CurrencyId1
                        AND @Currency2Id = CurrencyId2
                    )
        END TRY
    
        BEGIN CATCH
            IF @@Trancount > 0
                ROLLBACK TRAN
    
            DECLARE @ErrorBlockLineLen INTEGER = 0
            DECLARE @ErrorBlockGotTheFormat BIT = 0
            DECLARE @ErrorFormatIndent INTEGER = 3
            DECLARE @ErrorBlockBeenThrough INTEGER = NULL -- must be set to null
            DECLARE @ThisProcedureName VARCHAR(255) = ISNULL(OBJECT_NAME(@@PROCID), 'Testing')
            DECLARE @ErrorProc VARCHAR(4000) = CONVERT(VARCHAR(4000), ISNULL(ERROR_PROCEDURE(), @ThisProcedureName))
    
            WHILE @ErrorBlockGotTheFormat = 0
            BEGIN
                IF @ErrorBlockBeenThrough IS NOT NULL
                    SELECT @ErrorBlockGotTheFormat = 1
    
                SET @errormessage = Space(isnull(@ErrorFormatIndent, 0)) + @ThisProcedureName + ' Reports Error Thrown by: ' + @ErrorProc + CHAR(13)
                SET @errormessage += Space(isnull(@ErrorFormatIndent, 0)) + '-------->' + ISNULL(CONVERT(VARCHAR(4000), ERROR_MESSAGE()), 'Unknown') + '<--------' + CHAR(13) --               + Space(isnull(@ErrorFormatIndent,0)) + REPLICATE('=', @ErrorBlockLineLen) + CHAR(13) --               + Space(isnull(@ErrorFormatIndent,0)) + UPPER(@ThisProcedureName + ' Variable dump:') + CHAR(13) --               + Space(isnull(@ErrorFormatIndent,0)) + REPLICATE('=', @ErrorBlockLineLen) + CHAR(13) --
                    + CHAR(13) + Space(isnull(@ErrorFormatIndent, 0)) + '@Currency1ToCurrency2ExchangeRate:.....<' + ISNULL(CAST(@Currency1ToCurrency2ExchangeRate AS VARCHAR(25)), 'Null') + '>' + CHAR(13) + SPACE(ISNULL(@ErrorBlockBeenThrough, 0)) --
                    + CHAR(13) + Space(isnull(@ErrorFormatIndent, 0)) + '@Currency2Id:..........................<' + ISNULL(CAST(@Currency2Id AS VARCHAR(25)), 'Null') + '>' + CHAR(13) + SPACE(ISNULL(@ErrorBlockBeenThrough, 0)) --
                    + CHAR(13) + Space(isnull(@ErrorFormatIndent, 0)) + '@CurrencyId:...........................<' + ISNULL(CAST(@CurrencyId AS VARCHAR(25)), 'Null') + '>' + CHAR(13) + SPACE(ISNULL(@ErrorBlockBeenThrough, 0)) --
                    + CHAR(13) + Space(isnull(@ErrorFormatIndent, 0)) + '@CurrencyName:.........................<' + ISNULL(CAST(@CurrencyName AS VARCHAR(25)), 'Null') + '>' + CHAR(13) + SPACE(ISNULL(@ErrorBlockBeenThrough, 0)) --
    
                --SELECT @ErrorBlockLineLen = MAX(LEN(RTRIM(item)))
                --FROM dbo.fnSplit(@errormessage, CHAR(13))
                 SELECT @ErrorBlockLineLen = 120
    
                SELECT @ErrorBlockBeenThrough = 1
            END
    
                 RAISERROR ('%s' ,16 ,1 ,@ErrorMessage)
    
        END CATCH
    END
    GO
    

    这是要填充的代码。我对一些费率使用了倒数,因为它更容易。

    set nocount on
    
    exec usp_AddCurrency 'Rupiee'
    exec usp_AddCurrency 'Euro'
    exec usp_AddCurrency 'Dollar'
    exec usp_AddCurrency 'Pound'
    
    
    exec usp_AddCurrency "Rupiee", Null, 1, 1.000000
    exec usp_AddCurrency "Rupiee", Null, 2, 0.008000
    exec usp_AddCurrency "Rupiee", Null, 3, 0.009000
    exec usp_AddCurrency "Rupiee", Null, 4, 0.007000   
    
    Declare @Inverse Decimal(19,6)
    Select @Inverse =  cast ((1.000000/ (Select Conversion1To2Rate From #ConversionRates Where CurrencyId1 = 1 and CurrencyId2 =2 )) as decimal(19,6))
    exec usp_AddCurrency 'Euro'   , Null, 1, @Inverse
    exec usp_AddCurrency 'Euro'   , Null, 2, 1.000000
    exec usp_AddCurrency 'Euro'   , Null, 3, 1.090000
    exec usp_AddCurrency 'Euro'   , Null, 4, 0.850000
    
    
    Select @Inverse =  cast ((1.000000/ (Select Conversion1To2Rate From #ConversionRates Where CurrencyId1 = 1 and CurrencyId2 =3 )) as decimal(19,6))
    exec usp_AddCurrency 'Dollar' , Null, 1,@Inverse
    Select @Inverse =  cast ((1.000000/ (Select Conversion1To2Rate From #ConversionRates Where CurrencyId1 = 2 and CurrencyId2 =3 )) as decimal(19,6))
    exec usp_AddCurrency 'Dollar' , Null, 2, @Inverse
    exec usp_AddCurrency 'Dollar' , Null, 3, 1.000000
    exec usp_AddCurrency 'Dollar' , Null, 4, 0.770000
    
    Select @Inverse =  cast ((1.000000/ (Select Conversion1To2Rate From #ConversionRates Where CurrencyId1 = 1 and CurrencyId2 =4 )) as decimal(19,6))
    exec usp_AddCurrency 'Pound'  , Null, 1, @Inverse
    Select @Inverse =  cast ((1.000000/ (Select Conversion1To2Rate From #ConversionRates Where CurrencyId1 = 2 and CurrencyId2 =4 )) as decimal(19,6))
    exec usp_AddCurrency 'Pound'  , Null, 2, @Inverse
    Select @Inverse =  cast ((1.000000/ (Select Conversion1To2Rate From #ConversionRates Where CurrencyId1 = 3 and CurrencyId2 =4 )) as decimal(19,6))
    exec usp_AddCurrency 'Pound'  , Null, 3, @Inverse
    exec usp_AddCurrency 'Pound'  , Null, 4, 1.000000
    

    我想你的问题到此结束。 我用这段代码测试了输出:

    Declare @MySQL nVarChar(max) = 'Create Table ##output ([Currency Name] Varchar(255),' + char(13)
    
    SELECT @MySQL += STUFF(( SELECT  ',[' + CurrencyName + '] VARCHAR(255) '
                    FROM    #Currencies 
           order by Currencyid asc
                  FOR
                    XML PATH('')
                  ), 1, 1, '') + ')'
    
    
    exec sp_executesql @MySQL
    
    select * into #output from ##output
    Drop Table ##Output
    
    
    Declare @PivotThings varchar(max)
    
    
    select @PivotThings = STUFF(( SELECT  ',[' + CurrencyName + ']  '
                    FROM    #Currencies 
           order by Currencyid asc
                  FOR
                    XML PATH('')
                  ), 1, 1, '')
    
    Select @MySQL = '
     SELECT *
    FROM(
      select c1.CurrencyName  as Rows, c1.Currencyid as r,  c2.CurrencyName as Cols,   Conversion1To2Rate as [Value] from #ConversionRates r
    join #Currencies c1 on c1.Currencyid = r.CurrencyId1
    join #Currencies c2 on c2.Currencyid = r.CurrencyId2
    ) WorkOrders
    PIVOT
    (
      SUM(value)
      FOR [cols] IN (
        ##PivotThings##
        ) 
    
    ) AS PivotTable
    ORDER BY [r] 
    '
    
    
    Select @MySQL = REPLACE(@mysql , '##PivotThings##', @pivotthings)
    
    exec sp_executesql @mysql
    

    希望你觉得这很有用。这是我的第一篇文章 evah!

    【讨论】:

      猜你喜欢
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-04
      • 2012-05-01
      • 2010-10-03
      • 2019-12-01
      • 1970-01-01
      相关资源
      最近更新 更多