使用参数添加列?

Using Parameters to Add Columns?

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

table结构如下:

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,您只是选择了它。

您的代码容易受到 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

returns:

+------------+--------------+------------+----------+----------+----------+--------+
| 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   |
+------------+--------------+------------+----------+----------+----------+--------+

最好为您的 table 考虑一种不需要使用动态 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')

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

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

returns:

+--------------+----------+----------+----------+------------+
| 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   |
+--------------+----------+----------+----------+------------+

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

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

希望您觉得这很有用。这是我的第一个 post evah!