存储过程中的 Drop table 无法正常工作?
Drop table in Stored Procedure not working properly?
我有一个存储过程,如果 table 存在,它会删除 table 然后 re-creates table 并用相关数据填充它,我的一个朋友也差不多代码,唯一真正的区别在于 table.
的列 headers
作为示例,这是我的样子(不是真的,只是一个代表)。
+----+-----+-----+--------+
| 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
Invalid column name 'Foo'.
Msg 213, Level 16, State 1, Procedure XYZ, Line 61
Column name or number of supplied values does not match table definition.
然而,在两个存储过程的开始,我们有这样的:
CREATE PROCEDURE XYZ
AS
BEGIN
IF EXISTS (SELECT name
FROM sysobjects
WHERE name = 'TABLENAME'
AND xtype = 'u')
DROP TABLE TABLENAME;
据我了解,这应该删除整个 table?包括 table/column 定义和数据?
到目前为止我发现的唯一修复方法是在创建存储过程之前单独执行 DROP TABLE
,这对我们不起作用,因为它确实必须在存储过程中。
帮助将不胜感激:)
编辑:这是我的实际代码,除了注释,这正是它在我的脚本中的样子(不包括它背后的其他代码)。
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);
据我所知,错误的查询是插入,因为引擎找不到正确的 table 结构,请检查插入是否与您的第二个 table 示例具有相同的结构。不要忘记检查脚本开头的 USE,也许您正在使用不同的数据库,这可能会发生:)。
在最后一段代码中,您有
AND xtype = 'U'
如果您的排序规则区分大小写,则不会发生丢弃,因此会出现错误。
"however when creating the SP after using another, I get this error"(强调已添加。)
SQL 服务器将坚持认为存储过程与创建存储过程时 存在的 table 的定义相匹配。 如果 table创建存储过程时不存在,SQL 服务器将假定匹配的 table 将在 运行 时出现。
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'.
现在,删除 table 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.
如果您可以使用不同的 table 名称,请从那个开始。并且,如果 table 在 proc 执行后只存在片刻以便可以从中选择,则创建一个全局临时 table (即 table 名称以 ##
如 ##MyTable
).
但是,如果要求使用与你同学相同的 table 名字,那么老师可能是想让你了解延迟对象解析(即@Shannon 的回答)以及如何绕过它,因为除了学习这个,这个场景没有任何意义,因为在现实中人们永远不会做这样的事情。
子流程(即 EXEC
和 sp_executesql
)不会立即解析,因为它们在创建存储过程时不会执行。所以,简单地说,只需声明一个新的 NVARCHAR(MAX) 变量来保存一些 Dynamic SQL 并将你的 SELECT
语句放在那里。使用sp_executesql
传入@year
变量。您正在创建一个真正的 table,因此它将在子进程结束后继续存在,然后 ALTER TABLE
语句将起作用。
补充说明:
您实际上不需要 ALTER
语句来设置 [Richting] 字段的数据类型。只需告诉 SQL 服务器您的 SELECT 语句中的类型是什么:
CONVERT(NVARCHAR(8), '') AS [Richting]
您真的不想 CONVERT(NVARCHAR(8), OrderDate, 112)
与某个值进行比较,因为它会使 [OrderDate]
上的任何索引的使用无效。相反,从字符串构造一个日期值并将其转换为 DATETIME 或 DATE(即 CONVERT(DATETIME, @year + '0101')
)。
为了更好地理解这个问题,请阅读Sargability: Why %string% Is Slow, and at least the first link at the bottom, which is: What makes a SQL statement sargable?
您并不是真的想将 OrderDate 字段转换为 NVARCHAR(4) 只是为了比较年份,原因与刚才在上一点中提到的相同。至少使用 YEAR()
函数会更直接。但是如果你想确保可以使用索引,你不能在字段上放一个函数。但你只想要年份。那么年份不是和BETWEEN @Year + '0101' AND @Year + '1231'
一样吗? ;-)
有趣的是,第一个示例在 "What makes a SQL statement sargable?" S.O 中被接受。上一个项目符号中链接的问题正是我在这里推荐的问题:)。
我有一个存储过程,如果 table 存在,它会删除 table 然后 re-creates table 并用相关数据填充它,我的一个朋友也差不多代码,唯一真正的区别在于 table.
的列 headers作为示例,这是我的样子(不是真的,只是一个代表)。
+----+-----+-----+--------+
| 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
Invalid column name 'Foo'.Msg 213, Level 16, State 1, Procedure XYZ, Line 61
Column name or number of supplied values does not match table definition.
然而,在两个存储过程的开始,我们有这样的:
CREATE PROCEDURE XYZ
AS
BEGIN
IF EXISTS (SELECT name
FROM sysobjects
WHERE name = 'TABLENAME'
AND xtype = 'u')
DROP TABLE TABLENAME;
据我了解,这应该删除整个 table?包括 table/column 定义和数据?
到目前为止我发现的唯一修复方法是在创建存储过程之前单独执行 DROP TABLE
,这对我们不起作用,因为它确实必须在存储过程中。
帮助将不胜感激:)
编辑:这是我的实际代码,除了注释,这正是它在我的脚本中的样子(不包括它背后的其他代码)。
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);
据我所知,错误的查询是插入,因为引擎找不到正确的 table 结构,请检查插入是否与您的第二个 table 示例具有相同的结构。不要忘记检查脚本开头的 USE,也许您正在使用不同的数据库,这可能会发生:)。
在最后一段代码中,您有
AND xtype = 'U'
如果您的排序规则区分大小写,则不会发生丢弃,因此会出现错误。
"however when creating the SP after using another, I get this error"(强调已添加。) SQL 服务器将坚持认为存储过程与创建存储过程时 存在的 table 的定义相匹配。 如果 table创建存储过程时不存在,SQL 服务器将假定匹配的 table 将在 运行 时出现。
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'.
现在,删除 table 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.
如果您可以使用不同的 table 名称,请从那个开始。并且,如果 table 在 proc 执行后只存在片刻以便可以从中选择,则创建一个全局临时 table (即 table 名称以 ##
如 ##MyTable
).
但是,如果要求使用与你同学相同的 table 名字,那么老师可能是想让你了解延迟对象解析(即@Shannon 的回答)以及如何绕过它,因为除了学习这个,这个场景没有任何意义,因为在现实中人们永远不会做这样的事情。
子流程(即 EXEC
和 sp_executesql
)不会立即解析,因为它们在创建存储过程时不会执行。所以,简单地说,只需声明一个新的 NVARCHAR(MAX) 变量来保存一些 Dynamic SQL 并将你的 SELECT
语句放在那里。使用sp_executesql
传入@year
变量。您正在创建一个真正的 table,因此它将在子进程结束后继续存在,然后 ALTER TABLE
语句将起作用。
补充说明:
您实际上不需要
ALTER
语句来设置 [Richting] 字段的数据类型。只需告诉 SQL 服务器您的 SELECT 语句中的类型是什么:CONVERT(NVARCHAR(8), '') AS [Richting]
您真的不想
CONVERT(NVARCHAR(8), OrderDate, 112)
与某个值进行比较,因为它会使[OrderDate]
上的任何索引的使用无效。相反,从字符串构造一个日期值并将其转换为 DATETIME 或 DATE(即CONVERT(DATETIME, @year + '0101')
)。为了更好地理解这个问题,请阅读Sargability: Why %string% Is Slow, and at least the first link at the bottom, which is: What makes a SQL statement sargable?
您并不是真的想将 OrderDate 字段转换为 NVARCHAR(4) 只是为了比较年份,原因与刚才在上一点中提到的相同。至少使用
YEAR()
函数会更直接。但是如果你想确保可以使用索引,你不能在字段上放一个函数。但你只想要年份。那么年份不是和BETWEEN @Year + '0101' AND @Year + '1231'
一样吗? ;-)有趣的是,第一个示例在 "What makes a SQL statement sargable?" S.O 中被接受。上一个项目符号中链接的问题正是我在这里推荐的问题:)。