Not Exists 不能很好地处理多列

Posted

技术标签:

【中文标题】Not Exists 不能很好地处理多列【英文标题】:Not Exists not playing well with multiple columns 【发布时间】:2016-03-18 19:38:30 【问题描述】:

这一定是我刚刚错过的一些简单的事情......

我有一个临时表这样说:

CREATE TABLE #tsa
(
    AttendeeID int,
    SurveyID int,
    TrainingAttendeeID int
)

我使用 TOP 1 获得了一条类似这样的记录:

SELECT
        TOP 1
        @AttendeeID=ta.AttendeeID,
        @SurveyID=ts.SurveyID,
        @TrainingAttendeeID = ta.TrainingAttendeeID
    FROM            
        TrainingAttendee ta
    INNER JOIN
        [User] u
    ON
        u.UserID= ta.AttendeeID
    INNER JOIN
        [Training] t
    ON
        t.TrainingID = ta.TrainingID
    INNER JOIN
        [TrainingSet] ts
    ON
        ts.TrainingSetID = t.TrainingSetID
    WHERE
        ta.AttendedTraining = 1
        AND ta.ConfirmAttendedEmailOn IS NOT NULL
        --only get people who didn't fill out the survey
        AND NOT EXISTS (SELECT * FROM SurveyTaken st WHERE st.AddedByUserID = ta.AttendeeID AND st.SurveyID = ts.SurveyID)
    ORDER BY
        ta.AttendeeID,
        ts.SurveyID

一旦我得到这条记录,我就将它存储到我的临时表中:

--insert into our temp table
INSERT INTO #tsa(AttendeeID, SurveyID, TrainingAttendeeID) 
VALUES(@AttendeeID, @SurveyID, @TrainingAttendeeID)

然后我需要检查一些数据并发送电子邮件的整个过程......一旦发送电子邮件,我需要拿起下一条记录,不包括我之前的记录......所以没有显示太多代码:

WHILE SomeCondition
BEGIN
  --do some thing...

  --pick up next one
--grab next one
                SELECT
                    TOP 1
                    @AttendeeID = ta.AttendeeID,
                    @SurveyID=ts.SurveyID,
                    @TrainingAttendeeID=ta.TrainingAttendeeID
                FROM            
                    TrainingAttendee ta
                INNER JOIN
                    [User] u
                ON
                    u.UserID= ta.AttendeeID
                INNER JOIN
                    [Training] t
                ON
                    t.TrainingID = ta.TrainingID
                INNER JOIN
                    [TrainingSet] ts
                ON
                    ts.TrainingSetID = t.TrainingSetID
                WHERE
                    (
                        --same where as original
                        ta.AttendedTraining = 1
                        AND (ta.ConfirmAttendedEmailOn IS NOT NULL)
                        AND (NOT EXISTS (SELECT * FROM SurveyTaken st WHERE st.AddedByUserID = ta.AttendeeID AND st.SurveyID = ts.SurveyID))
                        --adding the piece such that we compare against the temp table...
                        AND (NOT EXISTS (SELECT * FROM #tsa tempS WHERE tempS.AttendeeID = ta.AttendeeID AND tempS.SurveyID = ts.SurveyID AND tempS.TrainingAttendeeID = ta.TrainingAttendeeID))
                    )
                ORDER BY
                    ta.AttendeeID,
                    ts.SurveyID

                --insert into our temp table    
                INSERT INTO #tsa(AttendeeID, SurveyID, TrainingAttendeeID) 
                VALUES(@AttendeeID, @SurveyID, @TrainingAttendeeID)
END

注意其中的 where 条件..我又添加了一个 AND...即:

--adding the piece such that we compare against the temp table...
AND (NOT EXISTS (SELECT * FROM #tSa tempS WHERE tempS.AttendeeID = ta.AttendeeID AND tempS.SurveyID = ts.SurveyID AND tempS.TrainingAttendeeID = ta.TrainingAttendeeID))

只是为了确保我不会重复使用我已经在我的临时表中处理过的记录......你会注意到我在最后也重新插入到我的临时表中......

--insert into our temp table    
INSERT INTO #tsa(AttendeeID, SurveyID, TrainingAttendeeID) 
VALUES(@AttendeeID, @SurveyID, @TrainingAttendeeID)

每次我运行这个存储过程时,它都会无限地运行,所以我相信此时我的情况有问题。我脑子里放了个屁……或者办公室里的噪音太大了。我在这里想念什么?我放置了一个打印语句,它一直在处理相同的记录......所以有些东西告诉我 where 子句中的最后一个条件不正确。

编辑

这是整个过程...我的问题是记录集中只有一条记录...但是存储过程继续处理同一条记录

PROCEDURE ScriptSendTrainingSurveyReminders 
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @AttendeeID int
    DECLARE @TrainingAttendeeID int
    DECLARE @SurveyID int
    DECLARE @Message nvarchar(MAX)
    DECLARE @Subject nvarchar(255)

        CREATE TABLE #tSa
        (
            AttendeeID int,
            SurveyID int,
            TrainingAttendeeID int
        )

    SELECT
        TOP 1
        @AttendeeID=ta.AttendeeID,
        @SurveyID=ts.SurveyID,
        @TrainingAttendeeID = ta.TrainingAttendeeID
    FROM            
        TrainingAttendee ta
    INNER JOIN
        [User] u
    ON
        u.UserID= ta.AttendeeID
    INNER JOIN
        [Training] t
    ON
        t.TrainingID = ta.TrainingID
    INNER JOIN
        [TrainingSet] ts
    ON
        ts.TrainingSetID = t.TrainingSetID
    WHERE
        ta.AttendedTraining = 1
        AND ta.ConfirmAttendedEmailOn IS NOT NULL
        --only get people who didn't fill out the survey
        AND NOT EXISTS (SELECT * FROM SurveyTaken st WHERE st.AddedByUserID = ta.AttendeeID AND st.SurveyID = ts.SurveyID)
    ORDER BY
        ta.TrainingAttendeeID,
        ta.AttendeeID,
        ts.SurveyID

    --insert into our temp table
    INSERT INTO #tSa(AttendeeID, SurveyID, TrainingAttendeeID) 
    VALUES(@AttendeeID, @SurveyID, @TrainingAttendeeID)

    WHILE @TrainingAttendeeID IS NOT NULL AND @AttendeeID IS NOT NULL AND @SurveyID IS NOT NULL
        BEGIN
            DECLARE @TrainingID int
            DECLARE @Title nvarchar(50)
            DECLARE @StartDateTime nvarchar(50)
            DECLARE @EndDateTime nvarchar(50)
            DECLARE @FullName nvarchar(255)
            DECLARE @EmailAddress nvarchar(255)
            DECLARE @Description nvarchar(MAX)

            --get the one record we are on...
            SELECT 
                @TrainingID = t.TrainingID,
                @Title = t.Title,
                @StartDateTime = CAST(CONVERT(DATE, t.StartDate) AS VARCHAR(50)) + ' ' + CAST(t.StartTimeHours AS VARCHAR(50)) + ':' + CAST(CASE WHEN LEN(t.StartTimeMinutes)=1 THEN  CAST(t.StartTimeMinutes AS VARCHAR(50)) + '0' ELSE CAST(t.StartTimeMinutes AS VARCHAR(50)) END AS VARCHAR(50)) + ' ' + CAST(t.StartTimeAMorPM AS VARCHAR(50)),
                @EndDateTime = CAST(CONVERT(DATE, t.EndDate) AS VARCHAR(50)) + ' ' + CAST(t.EndTimeHours AS VARCHAR(50)) + ':' + CAST(CASE WHEN LEN(t.EndTimeMinutes)=1 THEN  CAST(t.EndTimeMinutes AS VARCHAR(50)) + '0' ELSE CAST(t.EndTimeMinutes AS VARCHAR(50)) END AS VARCHAR(50)) + ' ' + CAST(t.EndTimeAMorPM AS VARCHAR(50)),
                @Description = t.Descriptionhtml,
                @FullName = u.FullName,
                @EmailAddress = u.EmailAddress
            FROM 
                Training t 
            INNER JOIN
                TrainingAttendee ta
            ON
                t.TrainingID = ta.TrainingID
            INNER JOIN
                [User] u
            ON
                u.UserID = ta.AttendeeID
            WHERE
                ta.TrainingAttendeeID = @TrainingAttendeeID

            IF @EmailAddress IS NOT NULL
                BEGIN 
                      --Email goes out here....
                END

            --grab next one
                SELECT
                    TOP 1
                    @AttendeeID = ta.AttendeeID,
                    @SurveyID=ts.SurveyID,
                    @TrainingAttendeeID=ta.TrainingAttendeeID
                FROM            
                    TrainingAttendee ta
                INNER JOIN
                    [User] u
                ON
                    u.UserID= ta.AttendeeID
                INNER JOIN
                    [Training] t
                ON
                    t.TrainingID = ta.TrainingID
                INNER JOIN
                    [TrainingSet] ts
                ON
                    ts.TrainingSetID = t.TrainingSetID
                WHERE
                    (
                        --same where as original
                        (ta.AttendedTraining = 1)
                        AND (ta.ConfirmAttendedEmailOn IS NOT NULL)
                        AND (NOT EXISTS (SELECT * FROM SurveyTaken st WHERE st.AddedByUserID = ta.AttendeeID AND st.SurveyID = ts.SurveyID))
                        --adding the piece such that we compare against the temp table...
                        AND (NOT EXISTS (SELECT * FROM #tSa tempS WHERE tempS.AttendeeID = ta.AttendeeID AND tempS.SurveyID = ts.SurveyID AND tempS.TrainingAttendeeID = ta.TrainingAttendeeID))
                    )
                ORDER BY
                    ta.TrainingAttendeeID,
                    ta.AttendeeID,
                    ts.SurveyID

                PRINT CAST('TrainingAttendeeID: ' + CAST(@TrainingAttendeeID as nvarchar(500)) + ' AttendeeID:' + CAST(@AttendeeID as nvarchar(500)) + ' SurveyID: ' + CAST(@SurveyID as nvarchar(500)) AS nvarchar(4000))
                --insert into our temp table    
                INSERT INTO #tSa(AttendeeID, SurveyID, TrainingAttendeeID) 
                VALUES(@AttendeeID, @SurveyID, @TrainingAttendeeID)
        END
END
GO

【问题讨论】:

标题看不懂……因为NOT EXIST (SELECT *NOT EXIST (SELECT 1是一样的 通常不这么说。但你有没有想过使用光标?对于select distinct AttendeeID, SurveyID, TrainingAttendeeID @JamieD77 - 我不想使用光标,我真的很想知道为什么这不能按预期工作...我会发布整个 sproc... @JuanCarlosOropeza-这不是我的问题,我可以做 SELECT 1...我的问题是为什么它一遍又一遍地重复同一记录的过程..我想我的不存在...会过滤掉它。 按照@Ivan 在他的回答中所说的去做,把SELECT @TrainingAttendeeID = NULL, @AttendeeID = NULL, @SurveyID = NULL 放在WHILE BEGIN 之后 【参考方案1】:

如果 select 不返回任何记录,变量将不会改变。我敢打赌它会循环处理最后一个@AttendeeID。

另一种测试方法 - 向临时表添加唯一约束。一旦没有更多记录可供选择,我认为循环将失败。

修复它的一种方法 - 在每次迭代开始时将 NULL 分配给所有变量(在 while 正文的顶部)。但如果可能的话,我建议将此代码重写为光标(不确定几个选择语句的逻辑是什么)。

请注意,代码块中的变量声明没有“块范围”意义,因为它不是 perl 或 python。

【讨论】:

以上是关于Not Exists 不能很好地处理多列的主要内容,如果未能解决你的问题,请参考以下文章

Plotly 不能很好地处理条形图的 NA。我该在哪里解决这个问题?

通过 talend 从 oracle 加载到 greenplum 时的数据不能很好地处理 CLOB 列

NLTK 正则表达式标记器在正则表达式中不能很好地处理小数点

当 sleep() 不能很好地与警报一起工作时,我还能做啥“睡眠”?

QVideoWidget 不能很好地调整大小

UISearchBarDisplayController 不能很好地与 UINavigationBar 配合使用