存储过程中的丢弃表无法正常工作?

时间:2015-01-12 17:12:15

标签: sql-server tsql stored-procedures ssms drop-table

我有一个存储过程会丢弃一个表(如果存在),然后重新创建表&用相关数据填充它,我的一个朋友有相同的代码,唯一真正的区别在于表的列标题。

作为一个例子,这是我的外表(不是真的,只是一种表现形式)。

+----+-----+-----+--------+
| ID | Foo | Bar | Number |
+----+-----+-----+--------+
|  1 | x   | x   |      0 |
|  2 | x   | x   |      1 |
+----+-----+-----+--------+

这就是他的样子

+----+--------+--------+-----+--------+
| ID | BarFoo | FooBar | Num | Suffix |
+----+--------+--------+-----+--------+
|  1 | x      | x      |   0 | a      |
|  2 | x      | x      |   1 | b      |
+----+--------+--------+-----+--------+

同样,这些仅仅是对情况的描述。

由于这是一项学校作业,老师将创建&执行两个SP,但是在使用另一个SP后创建SP时,我收到此错误:

  

Msg 207,Level 16,State 1,Procedure XYZ,Line 59
  列名称'Foo'无效。

     

Msg 213,Level 16,State 1,Procedure XYZ,Line 61
  列名或提供的值数与表定义不匹配。

但是,在两个存储过程开始时,我们都有:

CREATE PROCEDURE XYZ
AS
BEGIN
    IF EXISTS (SELECT name
               FROM   sysobjects
               WHERE  name = 'TABLENAME'
                      AND xtype = 'u')
        DROP TABLE TABLENAME;

根据我的理解,这应该删除整个表格?包括表/列定义&数据?

到目前为止,我发现的唯一修复是在创建存储过程之前单独执行DROP TABLE,这对我们不起作用,因为它实际上必须在存储过程中。

非常感谢帮助:)

编辑:这是我的ACTUAL代码,除了评论之外,这正是它在我的脚本中的样子(不包括其背后的其他代码)。

IF EXISTS (SELECT name
           FROM   sysobjects
           WHERE  name = 'BerekenStatistiek'
                  AND xtype = 'p')
    DROP PROCEDURE BerekenStatistiek;


GO
CREATE PROCEDURE BerekenStatistiek
@jaar INT=0
AS
BEGIN
    IF EXISTS (SELECT name
               FROM   sysobjects
               WHERE  name = 'Statistiek'
                      AND xtype = 'u')
        DROP TABLE Statistiek;
    DECLARE @year AS NVARCHAR (4);
    SET @year = CONVERT (NVARCHAR (4), @jaar);
    SELECT *,
           CAST (Kost - Korting + Freight AS MONEY) AS Netto,
           '' AS Richting
    INTO   Statistiek
    FROM   (SELECT   O.Kwartaal,
                     CAST (SUM(O.Kost) AS MONEY) AS Kost,
                     CAST (SUM(O.Korting) AS MONEY) AS Korting,
                     CAST (SUM(O.Freight) AS MONEY) AS Freight
            FROM     (SELECT CASE 
WHEN CONVERT (NVARCHAR (8), OrderDate, 112) BETWEEN @year + '0101' AND @year + '0331' THEN 1 
WHEN CONVERT (NVARCHAR (8), OrderDate, 112) BETWEEN @year + '0401' AND @year + '0630' THEN 2 
WHEN CONVERT (NVARCHAR (8), OrderDate, 112) BETWEEN @year + '0701' AND @year + '0930' THEN 3 
WHEN CONVERT (NVARCHAR (8), OrderDate, 112) BETWEEN @year + '1001' AND @year + '1231' THEN 4 
END AS 'Kwartaal',
                             ROUND(UnitPrice * Quantity, 2) AS Kost,
                             Round((UnitPrice * Quantity) * Discount, 2) AS Korting,
                             Freight
                      FROM   Orders AS O
                             INNER JOIN
                             OrderDetails AS Od
                             ON O.OrderID = Od.OrderID
                      WHERE  CONVERT (NVARCHAR (4), OrderDate, 112) = @year) AS O
            GROUP BY O.Kwartaal) AS O1;
    ALTER TABLE Statistiek ALTER COLUMN Kwartaal INT NOT NULL;
    ALTER TABLE Statistiek ALTER COLUMN Richting NVARCHAR (8);
    ALTER TABLE Statistiek
        ADD PRIMARY KEY (Kwartaal);
...

这是他的代码(为了便于阅读,排除了变量中值的插入(他的代码更笨重):

IF EXISTS (SELECT name
           FROM   sysobjects
           WHERE  name = 'BerekenStatistiek'
                  AND xtype = 'p')
    BEGIN
        DROP PROCEDURE BerekenStatistiek;
    END


GO
CREATE PROCEDURE BerekenStatistiek
@jaartal INT
AS
BEGIN
    DECLARE @huidigkwartaal AS INT = 1;
    DECLARE @beginmaand AS INT;
    DECLARE @eindmaand AS INT;
    DECLARE @vorige_netto_ontvangsten AS MONEY;
    IF EXISTS (SELECT *
               FROM   sysobjects
               WHERE  name = 'Statistiek'
                      AND xtype = 'U')
        BEGIN
            DROP TABLE Statistiek;
        END
    CREATE TABLE Statistiek
    (
        kwartaalnummer         INT          ,
        beginmaand             INT          ,
        eindmaand              INT          ,
        orderbedrag            MONEY        ,
        korting                MONEY        ,
        vervoerskost           MONEY        ,
        netto_ontvangsten      MONEY        ,
        stijgend_dalend_gelijk NVARCHAR (10)
    );

    --Variables get their data here.

    INSERT  INTO Statistiek (kwartaalnummer, beginmaand, eindmaand, orderbedrag, korting, vervoerskost, netto_ontvangsten, stijgend_dalend_gelijk)
    VALUES                 (@huidigkwartaal, @beginmaand, @eindmaand, @orderbedrag, @korting, @vervoerskost, @netto_ontvangsten, @stijgend_dalend_gelijk);

2 个答案:

答案 0 :(得分:0)

在最后一段代码中,您正在使用

AND xtype = 'U'

如果您的排序规则区分大小写,则不会发生丢弃,从而导致错误。

答案 1 :(得分:0)

如果您可以使用其他表名,请从该名称开始。并且,如果在执行proc之后该表必须只存在一段时间以便可以从中进行选择,那么创建一个全局临时表(即表名以##开头,如##MyTable中所示)

但是,如果要求使用与同学相同的表名,那么老师可能会尝试让您了解延迟对象解析(即@ Shannon的回答)以及如何绕过它,因为除了学习这个之外,这个场景毫无意义,因为人们永远不会在现实中做这样的事情。

子流程(即EXECsp_executesql)不会立即解决,因为在创建存储过程时它们不会被执行。因此,简单地说,只需声明一个新的NVARCHAR(MAX)变量来保存一些动态SQL并将SELECT语句放在那里。使用sp_executesql传递@year变量。您正在创建一个真实的表,以便它可以在子进程结束之后存活,然后ALTER TABLE语句将起作用。

附加说明:

  • 您并不需要ALTER语句来设置[Richting]字段的数据类型。只需告诉SQL Server SELECT语句中的类型:

    CONVERT(NVARCHAR(8), '') AS [Richting]
    
  • 您并非真的希望CONVERT(NVARCHAR(8), OrderDate, 112)与某个值进行比较,因为它会使[OrderDate]上可能存在的任何索引的使用无效。相反,从字符串构造日期值并将其转换为DATETIME或DATE(即CONVERT(DATETIME, @year + '0101'))。

    为了更好地理解这个问题,请阅读Sargability: Why %string% Is Slow,至少是底部的第一个链接,即:What makes a SQL statement sargable?

  • 您并不想将OrderDate字段转换为NVARCHAR(4)只是为了比较年份,原因与上面提到的相同。至少使用YEAR()函数会更直接。但是如果你想确保可以使用索引,你就不能在该字段上放置一个函数。但你只想要这一年。那么这一年与BETWEEN @Year + '0101' AND @Year + '1231'不一样吗? ; - )

    有趣的是,在#34;什么使得SQL语句可以被攻击?中接受的答案中的第一个例子?"所以。上一篇文章中链接的问题正是我在这里推荐的内容:)。