Mengapa kueri SQL Server saya berperilaku berbeda pada UPDATE daripada pada SELECT?

10

Saya telah menulis permintaan SQL Server yang memperbarui catatan untuk memiliki nomor urut setelah mempartisi di lapangan. Ketika saya menjalankannya sebagai pernyataan SELECT, semuanya tampak hebat:

DECLARE @RunDetailID INT = 448
DECLARE @JobDetailID INT

SELECT @JobDetailID = [JobDetailID] FROM [RunDetails] WHERE [RunDetailID] = @RunDetailID

SELECT
    [OrderedRecords].[NewSeq9],
    RIGHT([OrderedRecords].[NewSeq9], 4)
FROM
    (
        SELECT
            [Records].*,
            [Records].[SortField] + RIGHT('0000' + CAST(ROW_NUMBER() OVER(PARTITION BY [Records].[SortField] ORDER BY [Records].[RunDetailID], [Records].[SortField], [Records].[PieceID]) AS VARCHAR), 4) NewSeq9
        FROM
        (
            SELECT
                [MRDFStorageID], 
                [RunDetailID], 
                [SortField], 
                [PieceID], 
                [Seq9], 
                [BallotType]
            FROM
                [MRDFStorage]
                    JOIN [BallotStyles] ON [MRDFStorage].[SortField] = [BallotStyles].[Style] and [BallotStyles].[JobDetailID] = @JobDetailID
            WHERE
                [RunDetailID] IN (SELECT [RunDetailID] FROM [RunDetails] WHERE [JobDetailID] = @JobDetailID AND [RunStatusID] <> 0)
        ) Records
    ) OrderedRecords 
        JOIN MRDFStorage ON [OrderedRecords].[MRDFStorageID] = [MRDFStorage].[MRDFStorageID]
WHERE
    [MRDFStorage].[RunDetailID] = @RunDetailID

masukkan deskripsi gambar di sini

Namun, ketika saya membuat kueri menjadi perintah UPDATE, itu mulai melewatkan angka genap:

DECLARE @RunDetailID INT = 448
DECLARE @JobDetailID INT 

SELECT @JobDetailID = [JobDetailID] FROM [RunDetails] WHERE [RunDetailID] = @RunDetailID

UPDATE
    [MRDFStorage]
SET
    [Seq9] = [OrderedRecords].[NewSeq9],
    [Overlay1] = [OrderedRecords].[NewSeq9],
    [Overlay10] = RIGHT([OrderedRecords].[NewSeq9], 4)
FROM
    (
        SELECT
            [Records].*,
            [Records].[SortField] + RIGHT('0000' + CAST(ROW_NUMBER() OVER(PARTITION BY [Records].[SortField] ORDER BY [Records].[RunDetailID], [Records].[SortField], [Records].[PieceID]) AS VARCHAR), 4) NewSeq9
        FROM
        (
            SELECT
                [MRDFStorageID], 
                [RunDetailID], 
                [SortField], 
                [PieceID], 
                [Seq9], 
                [BallotType], 
                CAST([SpecialProcessing] as Int) StartCount
            FROM
                [MRDFStorage]
                    JOIN [BallotStyles] ON [MRDFStorage].[SortField] = [BallotStyles].[Style] and [BallotStyles].[JobDetailID] = @JobDetailID
            WHERE
                [RunDetailID] IN (SELECT [RunDetailID] FROM [RunDetails] WHERE [JobDetailID] = @JobDetailID AND [RunStatusID] <> 0)
        ) Records
    ) OrderedRecords 
        JOIN MRDFStorage ON [OrderedRecords].[MRDFStorageID] = [MRDFStorage].[MRDFStorageID]
WHERE
    [MRDFStorage].[RunDetailID] = @RunDetailID

masukkan deskripsi gambar di sini

Saya sudah mencoba secara khusus berfokus pada bagian ini:

[Records].[SortField] + RIGHT('0000' + CAST(ROW_NUMBER() OVER(PARTITION BY [Records].[SortField] ORDER BY [Records].[RunDetailID], [Records].[SortField], [Records].[PieceID]) AS VARCHAR), 4) NewSeq9

Apakah ada efek samping yang saya tidak ketahui?

DIPERBARUI DENGAN DEFINISI TABEL

CREATE TABLE [dbo].[MRDFStorage] (
    [MRDFStorageID]           INT            IDENTITY (1, 1) NOT NULL,
    [RunDetailID]             INT            NOT NULL,
    [PieceID]                 VARCHAR (15)   NULL,
    [SortField]               VARCHAR (20)   NULL,
    [BallotType]              VARCHAR (100)  NULL,
    [Seq9]                    VARCHAR (15)   NULL,
    CONSTRAINT [PK_MRDFStorage] PRIMARY KEY CLUSTERED ([MRDFStorageID] ASC),
    CONSTRAINT [FK_MRDFStorage_RunDetails] FOREIGN KEY ([RunDetailID]) REFERENCES [dbo].[RunDetails] ([RunDetailID])
);

CREATE TABLE [dbo].[BallotStyles] (
    [BallotStyleID]     INT           IDENTITY (1, 1) NOT NULL,
    [JobDetailID]       INT           NOT NULL,
    [Style]             VARCHAR (20)  NOT NULL,
    CONSTRAINT [PK_BallotStyles] PRIMARY KEY CLUSTERED ([BallotStyleID] ASC)
);

CREATE TABLE [dbo].[RunDetails] (
    [RunDetailID]        INT            IDENTITY (1, 1) NOT NULL,
    [JobDetailID]        INT            NOT NULL,
    CONSTRAINT [PK_RunDetails] PRIMARY KEY CLUSTERED ([RunDetailID] ASC)
);
Chris Schiffhauer
sumber
2
Hm, saya pikir Anda harus mencoba mengganti UPDATE [MRDFStorage]dengan UPDATE mdan JOIN MRDFStorage ON ...dengan. JOIN MRDFStorage m ON ...Saya khawatir UPDATE mungkin memperbarui beberapa baris lebih dari sekali. Baca postingan blog ini: Mari kita hapuskan UPDATE DARI!
ypercubeᵀᴹ

Jawaban:

4

Tidak melihat data yang Anda miliki membuat ini sedikit lebih sulit tetapi saya berhasil mereproduksi apa yang Anda lihat jika Anda melihatnya dengan ini:

insert into RunDetails(RunDetailID, JobDetailID) values(448, 1)
insert into MRDFStorage(RunDetailID, SortField) values(448, 'S1')
insert into MRDFStorage(RunDetailID, SortField) values(448, 'S1')
insert into MRDFStorage(RunDetailID, SortField) values(448, 'S1')
insert into BallotStyles(JobDetailID, Style) values(1, 'S1')
insert into BallotStyles(JobDetailID, Style) values(1, 'S1')

Faktor kunci di sini adalah dua baris di BallotStyles.

Anda bergabung ke BallotStylesdalam kueri paling dalam dan dengan data seperti di atas Anda akan mendapatkan baris duplikat untuk setiap baris di MRDFStorage. Karena Anda tidak menggunakan salah satu kolom di dalam BallotStylesAnda hanya memeriksa keberadaan baris dan itu dapat dilakukan dengan existsklausa sebagai gantinya dan itu tentu saja tidak akan membuat baris duplikat.

UPDATE
    [MRDFStorage]
SET
    [Seq9] = [OrderedRecords].[NewSeq9]
FROM
    (
        SELECT
            MRDFStorageID,
            [Records].[SortField] + RIGHT('0000' + CAST(ROW_NUMBER() OVER(PARTITION BY [Records].[SortField] ORDER BY [Records].[RunDetailID], [Records].[SortField], [Records].[PieceID]) AS VARCHAR), 4) NewSeq9
        FROM
        (
            SELECT
                M.[MRDFStorageID], 
                M.[RunDetailID], 
                M.[SortField], 
                M.[PieceID], 
                M.[BallotType]
            FROM
                [MRDFStorage] as M
-- Remove this join
--                    JOIN [BallotStyles] ON M.[SortField] = [BallotStyles].[Style] and [BallotStyles].[JobDetailID] = @JobDetailID
            WHERE
                [RunDetailID] IN (SELECT [RunDetailID] FROM [RunDetails] WHERE [JobDetailID] = @JobDetailID) and
-- Add this exists check instead of the join
                EXISTS (
                       SELECT *
                       FROM BallotStyles AS BS
                       WHERE M.SortField = BS.Style and
                             BS.JobDetailID = @JobDetailID
                       )
        ) Records
    ) OrderedRecords 
        JOIN MRDFStorage ON [OrderedRecords].[MRDFStorageID] = [MRDFStorage].[MRDFStorageID]
WHERE
    [MRDFStorage].[RunDetailID] = @RunDetailID
Mikael Eriksson
sumber