【问题标题】:Drop table in Stored Procedure not working properly?存储过程中的删除表无法正常工作?
【发布时间】:2015-03-10 12:45:36
【问题描述】:

我有一个存储过程,如果表存在则删除它,然后它重新创建表并用相关数据填充它,我的一个朋友有大约相同的代码,唯一真正的区别在于列标题桌子。

作为一个插图,这是我的样子(不是真的,只是一个表示)。

+----+-----+-----+--------+
| 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 时,我收到此错误:

消息 207,级别 16,状态 1,程序 XYZ,第 59 行
列名“Foo”无效。

消息 213,级别 16,状态 1,程序 XYZ,第 61 行
列名或提供的值的数量与表定义不匹配。

然而,在这两个存储过程的开始,我们有这样的:

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

据我了解,这应该删除整个表格?包括表/列定义和数据?

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

非常感谢您的帮助:)

编辑:这是我的实际代码,除了 cmets,这正是它在我的脚本中的样子(不包括它后面的其他代码)。

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);

【问题讨论】:

  • 您发布了错误的代码。该错误是当您将值放入表中时。大胆猜测一下,我会说您使用 sql 将旧版本表中的数据放在新表上,该表有更多列,没有一个称为 foo。
  • 嗯,是的,但是存储过程在处理任何数据之前都有DROP TABLE,所以要么我误解了DROP TABLE 的确切功能,要么存储过程不是' t考虑到这一点
  • 你的数据库的排序规则是什么?运行这个:SELECT CONVERT (varchar, SERVERPROPERTY('collation'));
  • 你没有从 Drop Table 中得到那个错误,你稍后会得到它。据说问题出在第 59 行和第 61 行,你只给了我们 5 行。这是创建的表与您尝试插入的数据之间的不匹配,不可能是其他原因。
  • @Sourav_Agasti Latin1_General_CI_AS @TonyHopkinson 正如我所说,这是因为 DROP TABLE 没有像我预期的那样执行,我知道每个版本中的列都不同,但是两个存储过程都有DROP TABLE一开始,据我所知应该没关系吧?

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


【解决方案1】:

“但是,当在使用另一个 SP 之后创建时,我得到了这个错误”(强调添加。) SQL Server 将坚持存储过程与在创建存储过程时存在的表的定义相匹配。如果在创建存储过程时表不存在,SQL Server 将假定匹配表将在运行时出现。

create table t (c int)
go
create procedure p as begin
    drop table t 
    select 1 as diff_column_name into t
    select diff_colun_name from t
end

结果:

Msg 207, Level 16, State 1, Procedure p, Line 6
Invalid column name 'diff_colun_name'.

现在,删除表t,然后创建过程:

drop table t 
go
create procedure p as begin
    drop table t 
    select 1 as diff_column_name into t
    select diff_colun_name from t
end

Command(s) completed successfully.

【讨论】:

  • 没错,这就是它在这一点上的表现,但是,我正在尝试找到一种不必单独删除表的方法,因此两个 SP 都可以背靠背执行。
  • @Yorrick 这些是编译错误,而不是执行错误。 当您保存/编译存储过程时,这些表需要存在,而不一定在您执行它们时存在。然而,解决这个问题有点困难和复杂。标准建议是不要尝试对同一个表有两个不同的定义,因为 SQL Server 不喜欢它。您可以使用 #temp 表解决很多问题(因为 T -SQL 编译器以不同方式处理它们),但如果您需要表持久化,它们需要保持一致的定义。
  • @Yorrick,要求很奇怪。在执行期间更改表结构并不常见。但是,如果表不存在。然后可以编译具有不同定义的两个使用它的过程 create procedure .... 然后可以运行这两个过程,第二个删除并重新创建具有其结构的表。
  • 老实说...你们中的任何一个人(@RBarryYoung 和@ShannonSeverance)读过任何关于其他答案的 cmets 吗?我无法知道/控制如何调用其他人表中的列,因为这是一个学校项目。我试图解决这个问题,这样我的老师就不会遇到问题......
【解决方案2】:

如果您可以使用不同的表名,请从该名称开始。并且,如果表必须在 proc 执行后仅存在片刻以便从中选择,则创建一个全局临时表(即表名以## 开头,如##MyTable)。

但是,如果要求使用与您的同学相同的表名,那么老师可能会试图让您了解延迟对象解析(即@Shannon 的答案)以及如何绕过它,因为在外面了解这一点后,这个场景就没有意义了,因为现实中永远不会做这样的事情。

子进程(即EXEC 和sp_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'不一样吗? ;-)

    有趣的是,“是什么让 SQL 语句可搜索?”中接受的答案中的第一个示例所以。上一个项目符号中链接的问题正是我在这里推荐的:)。

【讨论】:

  • 这不是必需的,但是我不知道其他人会如何称呼他们的表格,也不知道他们的表格结构将如何(我班大约有 15 个人,都在做这个)。我们还没有看到临时表,我真的不想使用我们没有真正看到的任何东西。不过我肯定会调查sp_executesql 的事情=)
  • @Yorrick 你有自己的架构吗?您不需要使用临时表来拥有唯一的名称,尤其是对于只有 15 个其他学生的情况。你可以叫它[djkfhgdjkfhgdjfhgjkdfg] 并且相当肯定没有其他人会使用这个名字;-)。
  • BETWEEN @year + '0101' AND @year + '0331' 在那里是因为 SP 需要用 4 条记录填充表格,给定年份的每个季度一个 :-) 另外,我确实可以给它一些荒谬的名字,但是在作业表名是特别说明的,我的老师并不是最宽容的人:-(
  • @Yorrick 我知道为什么CASE 语句中有 4 个BETWEEN 条件,我只是告诉您如何编写查询以获得相同的结果,同时允许 SQL Server 作为尽可能高效。我是说用CONVERT(DATETIME, @year + 'xxxx') 替换@year + 'xxxx' 的所有8 个实例,只使用[OrderDate] BETWEEN...,那里没有转换。我只建议了这个荒谬的名字,因为在你第一次替换时你说使用相同的名字是不是的要求。现在你说它是。没关系,我只是在理解你所说的话。
  • 哦,我明白了,我当时误会了一秒钟。是的,我很确定表名没有在分配中明确指定,但是我检查并证明它是,但是列名并不重要。
【解决方案3】:

据我所知,错误的查询是插入,因为引擎找不到正确的表结构,请检查插入是否具有与第二个表示例相同的结构。不要忘记在脚本开头检查 USE,也许您使用的是不同的数据库,这可能会发生:)。

【讨论】:

  • 两个脚本都有相同的用途,不过我会在我原来的问题中发布两个代码,包括实际的列名等。
  • 在第一个脚本中,您不会重新创建表,只需将其删除。我认为这是您运行脚本的错误顺序。
  • 在我自己的(第一个)脚本中,它是通过SELECT x INTO Statistiek 自动创建的,但是两者都不起作用,所以这不是“只有一个”的问题,而是两者都有的问题
【解决方案4】:

在最后一段代码中,你有

AND xtype = 'U'

如果您的排序规则区分大小写,则不会发生丢弃,因此会出现错误。

【讨论】:

  • 1 有xtype = 'u',另一个有xtype = 'U',但是两者都不起作用,所以我认为这不是问题所在。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多