如何/可以在T-SQL中执行此操作?
所以我有一张桌子如何/可以在T-SQL中执行此操作?,sql,sql-server,tsql,stored-procedures,database-design,Sql,Sql Server,Tsql,Stored Procedures,Database Design,所以我有一张桌子 CREATE TABLE Versions ( id INT IDENTITY(1,1), title NVARCHAR(100), PRIMARY KEY (id) ) 还有一张桌子 CREATE TABLE Questions ( id INT IDENTITY(1,1), subsection_id INT NOT NULL, qtext NVARCHAR(400) NOT NULL, version_id INT
CREATE TABLE Versions (
id INT IDENTITY(1,1),
title NVARCHAR(100),
PRIMARY KEY (id)
)
还有一张桌子
CREATE TABLE Questions (
id INT IDENTITY(1,1),
subsection_id INT NOT NULL,
qtext NVARCHAR(400) NOT NULL,
version_id INT NOT NULL,
viewtype INT NOT NULL DEFAULT 1,
PRIMARY KEY (id),
FOREIGN KEY (subsection_id) REFERENCES Subsections(id),
FOREIGN KEY (version_id) REFERENCES Versions(id)
);
它的视觉表现像
-- Questions
-- ============================================================================================================================
-- id | qtext | subsection_id | version_id | viewtype
-- =============================================================================================================================
-- 1 | 'Does Hillary Clinton look good in orange?' | 1 | 1 | 1
-- 2 | 'How many prime numbers are there?' | 1 | 1 | 1
-- 3 | 'What do I suck at writing SQL?' | 1 | 1 | 1
-- 4 | 'Would Jon Skeet beat Mark Zuckerberg in a programming contest?' | 1 | 1 | 1
我需要的是一个过程,既向Versions表中插入新行,又向Questions表中添加所有当前行,并适当增加ID,版本ID等于刚刚创建的版本的ID
例如:
如果版本来自
id | title
-----------
1 | "V1"
2 | "V2"
然后问题就来了
利用我有限的数据库技能的最佳尝试:
CREATE PROCEDURE OntoNewVersion
@new_title NVARCHAR(100)
AS
INSERT INTO Versions (title) VALUES (@new_title)
SET @versid = SCOPE_IDENTITY()
SET @qrows = SELECT COUNT(*) FROM Questions; -- this is wrong, I know
SET @i = 1;
WHILE @i <= @qrows
BEGIN
SET @thisq = SELECT * FROM Questions WHERE id=@i
INSERT INTO Questions (qtext,subsection_id,version_id,viewtype) VALUES (@thisq.qtext,@thisq.subsection_id,@versid,@thisq.viewtype);
END
这接近正确吗?什么需要改变?有没有更好的方法呢?您可以尝试下面的查询
CREATE PROCEDURE OntoNewVersion
@new_title NVARCHAR(100)
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION T1
DECLARE @versid INT
INSERT INTO Versions (title) VALUES (@new_title)
SET @versid = SCOPE_IDENTITY()
INSERT INTO Questions
(qtext,subsection_id,version_id,viewtype)
SELECT qtext, subsection_id,@versid,viewtype
FROM Questions
COMMIT TRANSACTION T1
END TRY
BEGIN CATCH
IF(@@TRANCOUNT>0)
BEGIN
ROLLBACK TRANSACTION T1
END
;THROW
END CATCH
END
那是行不通的。事实上,这里根本不需要循环。您应该看看如何使用输出。请看一看我的答案,为什么你要创建多个问题的副本?似乎您在这里遇到了一些规范化问题。@SeanLange否。我不想详细介绍业务逻辑,但基本上,这个过程相当于将问题移到一个新版本上,初始化为旧问题,并将旧问题存储起来以供报告之用。然后,您需要确定要复制哪一组问题。你把它编码的方式会成倍地增加问题的数量。第一次用4个问题运行此程序时,您将有8个问题,下一次将有16个问题,等等…除非您包含where子句以将问题限制在某个组。为什么建议使用MAXId而不是您发布的内容?你发布的是正确的方法。使用MAXId容易出现竞争条件和并发性。这不是一个好方法。这纯粹是因为一个无关的个人经历,一个失败的交易导致我的身份值仍然增加,并持有一个实际上不存在的假值。哈???SCOPE_IDENTITY将返回当前作用域中的最后一个标识值。如果插入失败,仍将返回该值,但未插入该行。处理这个问题的方法是把整个事情放在一个事务中。这样,如果“插入到版本”失败,则“插入到问题”也将回滚。@SeanLange您是对的!在像这个问题这样的情况下,SCOPE_标识将是添加事务和TRY/CATCH的方式,这是坚如磐石的。
CREATE PROCEDURE OntoNewVersion
@new_title NVARCHAR(100)
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION T1
DECLARE @versid INT
INSERT INTO Versions (title) VALUES (@new_title)
SET @versid = SCOPE_IDENTITY()
INSERT INTO Questions
(qtext,subsection_id,version_id,viewtype)
SELECT qtext, subsection_id,@versid,viewtype
FROM Questions
COMMIT TRANSACTION T1
END TRY
BEGIN CATCH
IF(@@TRANCOUNT>0)
BEGIN
ROLLBACK TRANSACTION T1
END
;THROW
END CATCH
END