如何在SQL中编写“标记”查询?

时间:2009-02-17 17:13:12

标签: sql sql-server-2005 tsql stored-procedures

我在我的网络应用中添加了“标记”功能。我的应用程序表结构如下:

Tag:

(TagId INT IDENTITY, TagName VARCHAR(60))

TaggedRecords:

(TaggedId INT IDENTITY, TagId, TaggedRecordId)

现在,我希望当有人向任何记录添加标记时,应该使用单个sql查询或使用存储过程执行以下操作;

  • 标签是否已经出现在“标签”表中?
  • 如果标签存在,则在“TaggedRecords”表中插入一行
  • 否则,如果标签不存在,则首先在“Tag”表中插入标签,然后获取新添加标签的Id并在“TaggedRecord”表中插入记录

基本上,我更感兴趣的是使用单个查询或最多两个sql查询来执行这些操作。我不想在sql存储过程中创建多个If-Else条件。

谢谢,

4 个答案:

答案 0 :(得分:1)

你基本上已经掌握了它,但你可以通过改变顺序使其更有效率。这是您的过程的一些伪SQL代码:

SELECT TagId FROM Tag WHERE TagName = @NewTag into @TagId

IF @TagId IS NULL THEN
    INSERT new tag, returning the new TagId into @TagId

INSERT new record into TaggedRecords

不确定为什么你对使用if子句如此不利......


要将新生成的TagId放入变量中,我们有两个选项,因为您使用的是MSSQL:

    INSERT INTO Tag (TagName) 
    OUTPUT Inserted.TagId INTO @TagId 
    VALUES (@NewTag);
  • 标签是唯一的,因此您应该只能将其选出:
    INSERT INTO Tag (TagName) VALUES (@NewTag);
    SELECT TagId FROM Tag WHERE TagName = @NewTag INTO @TagId;

如果要将其退出,如果使用IF EXISTS子句,则该过程可能会更好地读取,但实质上您无论如何都要做同样的工作:

IF NOT EXISTS(SELECT TagId FROM Tag WHERE TagName = @NewTag)
BEGIN
    INSERT INTO Tag (TagName) VALUES (@NewTag);
    SET @TagId = (SELECT TagId FROM Tag WHERE TagName = @NewTag);
END
ELSE
BEGIN
    SET @TagId = (SELECT TagId FROM Tag WHERE TagName = @NewTag);
END

INSERT new record into TaggedRecords

答案 1 :(得分:1)

这个怎么样......

CREATE PROC TagMe(@TagName VARCHAR(100),@TaggedRecordId INT)
AS

DECLARE @TagId INT

SET @TagId = SELECT TagId FROM Tag WHERE TagName = @TagName

IF @TagId IS NOT NULL
    BEGIN
    --Tag exists
        INSERT INTO TaggedRecords (TagId, TaggedRecordId) VALUES(@TagId,@TaggedRecordId)
    RETURN
    END
ELSE
    -- New tag
    BEGIN
        INSERT INTO Tag (TagName) OUTPUT inserted.id INTO @TagId VALUES(@TagName)
        INSERT INTO TaggedRecords (TagId, TaggedRecordId) VALUES(@TagId,@TaggedRecordId)
    RETURN
END

没有对此进行测试,但理论应该是合理的:)

有关OUTPUT用法的另一个示例,请参阅this link

上的帖子

修改

根据以下评论,此版本使用EXISTS ...

CREATE PROC TagMe(@TagName VARCHAR(100),@TaggedRecordId INT)
AS

DECLARE @TagId INT

IF EXISTS(SELECT TagId FROM Tag WHERE TagName = @TagName)
    BEGIN
    --Tag exists
        SET @TagId = SELECT TagId FROM Tag WHERE TagName = @TagName  
        INSERT INTO TaggedRecords (TagId, TaggedRecordId) VALUES(@TagId,@TaggedRecordId)
    RETURN
    END
ELSE
    -- New tag
    BEGIN
        INSERT INTO Tag (TagName) OUTPUT inserted.id INTO @TagId VALUES(@TagName)
        INSERT INTO TaggedRecords (TagId, TaggedRecordId) VALUES(@TagId,@TaggedRecordId)
    RETURN
END

虽然我不确定(我认为我在这里反对自己,但这些东西中有很多是“依赖于”答案!)。这个例子实际上最适合更多地使用新标签,因为它只执行一次EXISTS然后继续,而对于现有标签,它将执行EXISTS然后执行SELECT。

嗯,接受你的选择 - 或在体积下测试两种方法:)

答案 2 :(得分:0)

我可以问为什么? if / else有限制吗? :)

看一下你的情况,它看起来不需要超过1个IF条款。

答案 3 :(得分:0)

您可以在标记表中使用合并,然后在标记记录表中插入。这是Oracle语法,所以你必须调整它,但想法是一样的:

MERGE INTO TAG
USING (SELECT @tagname as TAGNAME FROM DUAL) RECORD
ON (TAG.TAGNAME = RECORD.TAGNAME)
WHEN NOT MATCHED THEN INSERT (TAG.TAGNAME) VALUES (@tagname);

INSERT INTO TAGGEDRECORDS(TAGID) VALUES (SELECT TAGID FROM TAG WHERE TAGNAME=@tagname);

我认为SQL Server在MERGE语句中也支持更多功能,所以你可以用WHEN MATCHED(插入taggedrecords表)子句做一些事情。

退房 MSDN link