0

我有一个基于 SQL 的小挑战,我正在尝试解决它以更好地了解动态 SQL。

我的要求如下。

我创建了一个如下所示的表:

CREATE TABLE Prison_Doors
(
 DoorNum INT IDENTITY(1,1) PRIMARY KEY,
 DoorOpen BIT,
 DoorClosed BIT,
 Trips INT
 )
 GO

我需要创建一个动态 SQL Proc 来插入 50 个门号并将它们分配为已关闭。

proc的预期结果:

|DoorNum|DoorOpen|DoorClosed|Trips|
|-------|--------|----------|-----|
|   1   |    0   |     1    |null |
|-------|--------|----------|-----|
|---------All the way to 50-------|
|-------|--------|----------|-----|
|   50  |    0   |     1    |null |

这是我写的,但它没有插入:

BEGIN
DECLARE @SQL VARCHAR(8000)
DECLARE @Index INT
SET @Index=1


WHILE (@Index<=50)
BEGIN
    SET @SQL= 'INSERT INTO Prison_Doors(DoorNum,DoorOpen,DoorClosed)
                    VALUES('+CAST(@Index AS VARCHAR)+',0,1),'
    SET @Index=@Index+1
END

SET @SQL = SUBSTRING(@SQL, 1, LEN(@SQL)-1)
EXEC(@SQL)

结尾

我想知道我做错了什么。

完成所有这些之后,我需要运行另一个循环,从一号门开始,每隔一扇门打开一次,然后将行程改为一,然后增加到每 3 扇门打开,行程变为 2,这种递增一直持续到所有门是打开的,然后将选择它所花费的旅行次数。

我希望有人可以帮助我,因为我是 Dynamic SQL 的新手,我只需要一些指导而不是完整的解决方案。

非常感谢您的帮助:)

4

2 回答 2

3

你得到的错误是因为你试图插入一个标识列DoorNumber

DoorNum INT IDENTITY(1,1) PRIMARY KEY,

从列列表中删除该列,而不是:

INSERT INTO Prison_Doors(DoorNum,DoorOpen,DoorClosed)

删除该列DoorNum

INSERT INTO Prison_Doors(DoorOpen,DoorClosed)
...

但是,不需要动态 SQL 来执行此操作,您可以使用如下锚表执行此操作:

 WITH temp 
 AS
 (
   SELECT n
   FROM (VALUES(1), (2), (3), (4)) temp(n)
 ), nums
 AS
 (
   SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS n
   FROM temp t1, temp t2, temp t3
 )
 INSERT INTO Prison_Doors(DoorOpen, DoorClosed)
 SELECT 0 AS DoorOpen, 1 AS DoorClosed
 FROM nums
 WHERE n <= 50;

现场演示。


更新:

我的代码逐行做什么?

生成数字序列:

第一个问题是生成从 1 到 50 的 50 个数字的序列,我使用了一个只有四行从 1 到 4 的锚表,如下所示:

SELECT n
FROM (VALUES(1), (2), (3), (4)) temp(n);

这种使用 SQL-Server-2008 的语法VALUES是新的,它被称为行值构造函数。在 之后VALUES,您分配表的别名和括号中的目标列,例如temp(n)

对于旧版本,您必须使用类似的东西:

SELECT n
FROM 
(
   SELECT 1 AS n
   UNION ALL 
   SELECT 2
   UNION ALL
   SELECT 3
   UNION ALL 
   SELECT 4
) AS temp;

这将只给你 4 行,但我们需要生成 50 行。这就是为什么我CROSS JOIN这个表使用了 3 次:

FROM temp t1, temp t2, temp t3

它与

FROM temp t1
CROSS JOIN temp t2
CROSS JOIN temp t3

这会将这些行乘以 64 次,4 行3 = 64 行:

1   1   1
1   2   1
1   3   1
1   4   1
2   1   1
2   2   1
2   3   1
2   4   1
....
3   1   4
3   2   4
3   3   4
3   4   4
4   1   4
4   2   4
4   3   4
4   4   4

功能的使用ROW_NUMBER()

然后使用ROW_NUMBER()会给我们一个从 1 到 64 的排名数字,如下所示:

 WITH temp 
 AS
 (
   SELECT n
   FROM (VALUES(1), (2), (3), (4)) temp(n)
 )
 SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS n
 FROM temp t1, temp t2, temp t3;

请注意:ROW_NUMBER需要一个ORDER BY子句,否则顺序无关紧要,所以我使用了(SELECT 1).

还有另一种不使用 来生成序列号的方法ROW_NUMBER,它也取决于CROSS JOIN,具有如下锚表:

 WITH temp 
 AS
 (
   SELECT n
   FROM (
         VALUES(0), (1), (2), (3), (4), (5), (6), (7), (8), (9)
        ) temp(n)
 ), nums
 AS
 (
   SELECT t1.n * 100 + t2.n * 10 + t3.n + 1 AS n
   FROM temp t1, temp t2, temp t3
 )
 SELECT n
 FROM nums 
 ORDER BY n;

另一种生成数字序列的方法


常用表表达式:

CTE称为通用表表达式,在SQL Server 2005中引入,是SQL Server支持的表表达式类型之一。其他三个是:

  • 派生表,
  • 意见,和
  • 内联表值函数。

它有很多重要的优点。其中之一是让您创建一个虚拟表,以后可以重复使用它们,就像我所做的那样:

 WITH temp 
 AS
 (
   SELECT n
   FROM (VALUES(1), (2), (3), (4)) temp(n)
 ), nums
 AS
 (
   SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS n
   FROM temp t1, temp t2, temp t3
 )
 ...

在这里,我定义了两个 CTE ,temp然后定义了另一个numstemp所以这个 OL,你可以创建多个 CTE,只需放一个分号,然后是一个带有AS子句和两个的新 CTE ()


使用从另一个表插入一个表INSERT INTO ... SELECT ...

现在,我们有一个nums从 1 到 64 行的虚拟表,我们需要插入从 1 到 50 的行。

为此,您可以使用INSERT INTO ... SELECT ....

请注意,INSERT子句中的列是可选的,但是如果这样做,则必须为每一行输入一个值,否则会出现错误,例如,如果您有四列,而VALUES子句中只输入了三个值或者在SELECT子句中,那么你会得到一个错误。这对于定义如下的 idenetityt 列无效:

Identity(1,1)
         ^ ^
         | |
         | ------------------The seed
         The start

在这种情况下,您只需忽略子句中列列表中的该列INSERT,它将具有标识值。有一个选项可以让您手动插入一个值,就像在@Raj's answer中一样。

请注意:在我的回答中,我没有将序列号插入该列,而是将值插入 50 次。但是由于 Identity 列,实际数字会自动生成:

 ...
 INSERT INTO Prison_Doors(DoorOpen, DoorClosed)
 SELECT 0 AS DoorOpen, 1 AS DoorClosed
 FROM nums;
 WHERE n <= 50;
于 2013-01-30T07:51:03.830 回答
0

首先,您已将 DoorNum 声明为 Identity,然后您尝试将值显式插入该列。SQL Server 不允许这样做,除非您选择

SET IDENTITY_INSERT ON

试试这个查询。它应该插入你想要的 50 行

DECLARE @SQL VARCHAR(8000)
DECLARE @Index INT
SET @Index=1


WHILE (@Index<=50)
BEGIN
    SET @SQL= 'INSERT INTO Prison_Doors(DoorOpen,DoorClosed)VALUES(0,1)'
EXEC(@SQL)
SET @Index=@Index+1
END

拉吉

于 2013-01-30T07:51:54.813 回答