it-swarm.asia

INSERT efisien menjadi tabel dengan indeks Clustered

Saya memiliki pernyataan SQL yang menyisipkan baris ke tabel dengan indeks berkerumun di kolom TRACKING_NUMBER.

MISALNYA.:

INSERT INTO TABL_NAME (TRACKING_NUMBER, COLB, COLC) 
SELECT TRACKING_NUMBER, COL_B, COL_C 
FROM STAGING_TABLE

Pertanyaan saya adalah - apakah itu membantu untuk menggunakan klausa ORDER BY dalam pernyataan SELECT untuk kolom indeks berkerumun, atau apakah laba yang diperoleh akan dinegasikan oleh jenis tambahan yang diperlukan untuk klausa ORDER BY?

29
GWR

Karena jawaban lain sudah menunjukkan SQL Server mungkin atau mungkin tidak secara eksplisit memastikan bahwa baris diurutkan dalam urutan indeks berkerumun sebelum insert.

Ini tergantung pada apakah atau tidak operator indeks berkerumun dalam paket memiliki set properti DMLRequestSort (yang pada gilirannya tergantung pada perkiraan jumlah baris yang dimasukkan).

Jika Anda menemukan bahwa SQL Server meremehkan ini untuk alasan apa pun Anda mungkin mendapat manfaat dari menambahkan ORDER BY eksplisit ke kueri SELECT untuk meminimalkan pemisahan halaman dan fragmentasi berikutnya dari operasi INSERT

Contoh:

use tempdb;

GO

CREATE TABLE T(N INT PRIMARY KEY,Filler char(2000))

CREATE TABLE T2(N INT PRIMARY KEY,Filler char(2000))

GO

DECLARE @T TABLE (U UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(),N int)

INSERT INTO @T(N)
SELECT number 
FROM master..spt_values
WHERE type = 'P' AND number BETWEEN 0 AND 499

/*Estimated row count wrong as inserting from table variable*/
INSERT INTO T(N)
SELECT T1.N*1000 + T2.N
FROM @T T1, @T T2

/*Same operation using explicit sort*/    
INSERT INTO T2(N)
SELECT T1.N*1000 + T2.N
FROM @T T1, @T T2
ORDER BY T1.N*1000 + T2.N


SELECT avg_fragmentation_in_percent,
       fragment_count,
       page_count,
       avg_page_space_used_in_percent,
       record_count
FROM   sys.dm_db_index_physical_stats(2, OBJECT_ID('T'), NULL, NULL, 'DETAILED')
;  


SELECT avg_fragmentation_in_percent,
       fragment_count,
       page_count,
       avg_page_space_used_in_percent,
       record_count
FROM   sys.dm_db_index_physical_stats(2, OBJECT_ID('T2'), NULL, NULL, 'DETAILED')
;  

Menunjukkan bahwa T terfragmentasi secara besar-besaran

avg_fragmentation_in_percent fragment_count       page_count           avg_page_space_used_in_percent record_count
---------------------------- -------------------- -------------------- ------------------------------ --------------------
99.3116118225536             92535                92535                67.1668272794663               250000
99.5                         200                  200                  74.2868173956017               92535
0                            1                    1                    32.0978502594514               200

Tetapi untuk T2 fragmentasi minimal

avg_fragmentation_in_percent fragment_count       page_count           avg_page_space_used_in_percent record_count
---------------------------- -------------------- -------------------- ------------------------------ --------------------
0.376                        262                  62500                99.456387447492                250000
2.1551724137931              232                  232                  43.2438349394613               62500
0                            1                    1                    37.2374598468001               232

Sebaliknya kadang-kadang Anda mungkin ingin memaksa SQL Server untuk meremehkan jumlah baris ketika Anda tahu data sudah dipilah sebelumnya dan ingin menghindari jenis yang tidak perlu. Salah satu contoh penting adalah ketika memasukkan sejumlah besar baris ke dalam tabel dengan kunci indeks yang dikelompokkan newsequentialid. Dalam versi SQL Server sebelum Denali SQL Server menambahkan operasi sortir yang tidak perlu dan berpotensi mahal . Ini bisa dihindari dengan

DECLARE @var INT =2147483647

INSERT INTO Foo
SELECT TOP (@var) *
FROM Bar

SQL Server kemudian akan memperkirakan bahwa 100 baris akan dimasukkan terlepas dari ukuran Bar yang berada di bawah ambang di mana semacam ditambahkan ke rencana. Namun seperti yang ditunjukkan dalam komentar di bawah ini, ini berarti bahwa sisipan sayangnya tidak akan dapat memanfaatkan penebangan minimal.

18
Martin Smith

Jika pengoptimal memutuskan akan lebih efisien untuk menyortir data sebelum dimasukkan, ia akan melakukannya di suatu tempat di hulu dari operator penyisipan. Jika Anda memperkenalkan pengurutan sebagai bagian dari kueri, pengoptimal harus menyadari bahwa data sudah diurutkan dan dihilangkan melakukannya lagi. Perhatikan bahwa rencana eksekusi yang dipilih dapat bervariasi dari satu menjalankan ke menjalankan tergantung pada jumlah baris yang dimasukkan dari tabel pementasan Anda.

Jika Anda dapat menangkap rencana pelaksanaan proses dengan dan tanpa jenis yang eksplisit, lampirkan pada pertanyaan Anda untuk dikomentari.

Edit: 2011-10-28 17:00

@ jawaban Gonsalu muncul untuk menunjukkan bahwa operasi semacam selalu terjadi, ini bukan masalahnya. Diperlukan skrip demo!

Karena skrip semakin besar, saya memindahkannya ke Gist . Untuk memudahkan percobaan, skrip menggunakan mode SQLCMD. Tes dijalankan pada 2K5SP3, dual core, 8GB.

Tes memasukkan mencakup tiga skenario:

  1. Staging data clustered index dalam urutan yang sama dengan target.
  2. Pementasan data indeks berkerumun dalam urutan terbalik.
  3. Pementasan data dikelompokkan oleh col2 yang berisi INT acak.

Jalankan pertama, memasukkan 25 baris.

1st run, 25 rows

Ketiga paket eksekusi adalah sama, tidak ada penguraian yang terjadi di mana pun dalam paket dan pemindaian indeks berkerumun adalah "dipesan = salah".

Jalankan kedua, memasukkan 26 baris.

2nd run, 26 rows

Kali ini rencananya berbeda.

  • Yang pertama menunjukkan pemindaian indeks berkerumun sebagai memerintahkan = false. Tidak ada penyortiran yang terjadi karena sumber data diurutkan dengan tepat.
  • Dalam kedua pemindaian indeks berkerumun seperti yang diperintahkan = true, mundur. Jadi kami tidak memiliki operasi pengurutan tetapi kebutuhan untuk data yang akan disortir diakui oleh pengoptimal dan itu memindai dalam urutan terbalik.
  • Yang ketiga menunjukkan operator pengurutan.

Jadi, ada titik kritis di mana pengoptimal menganggap semacam itu perlu. Seperti yang ditunjukkan @MartinSmith, ini tampaknya didasarkan pada perkiraan baris yang akan dimasukkan. Pada rig pengujian saya 25 tidak memerlukan pengurutan, 26 tidak (2K5SP3, dual core, 8GB)

Skrip SQLCMD mencakup variabel yang memungkinkan ukuran baris dalam tabel untuk berubah (mengubah kepadatan halaman) dan jumlah baris dalam dbo.MyTable sebelum sisipan tambahan. Dari pengujian saya, tidak ada yang berpengaruh pada titik kritis.

Jika ada pembaca yang cenderung, silakan jalankan skrip dan tambahkan titik kritis Anda sebagai komentar. Tertarik untuk mendengar jika bervariasi di seluruh rig uji dan/atau versi.

Edit: 2011-10-28 20:15

Tes berulang pada rig yang sama tetapi dengan 2K8R2. Kali ini titik kritis adalah 251 baris. Sekali lagi, memvariasikan kepadatan halaman dan jumlah baris yang ada tidak berpengaruh.

12

Klausa ORDER BY Dalam pernyataan SELECT berlebihan.

Itu berlebihan karena baris yang akan dimasukkan, jika mereka perlu disortir, tetap disortir.

Mari kita buat test case.

CREATE TABLE #Test (
    id INTEGER NOT NULL
);

CREATE UNIQUE CLUSTERED INDEX CL_Test_ID ON #Test (id);

CREATE TABLE #Sequence (
    number INTEGER NOT NULL
);

INSERT INTO #Sequence
SELECT number FROM master..spt_values WHERE name IS NULL;

Mari kita aktifkan tampilan teks dari rencana kueri yang sebenarnya, sehingga kita bisa melihat tugas apa yang dilakukan oleh prosesor kueri.

SET STATISTICS PROFILE ON;
GO

Sekarang, mari INSERT 2K baris ke tabel tanpa klausa ORDER BY.

INSERT INTO #Test
SELECT number
  FROM #Sequence

Paket eksekusi aktual untuk permintaan ini adalah sebagai berikut.

INSERT INTO #Test  SELECT number    FROM #Sequence
  |--Clustered Index Insert(OBJECT:([tempdb].[dbo].[#Test]), SET:([tempdb].[dbo].[#Test].[id] = [tempdb].[dbo].[#Sequence].[number]))
       |--Top(ROWCOUNT est 0)
            |--Sort(ORDER BY:([tempdb].[dbo].[#Sequence].[number] ASC))
                 |--Table Scan(OBJECT:([tempdb].[dbo].[#Sequence]))

Seperti yang Anda lihat, ada operator Sortir sebelum INSERT yang sebenarnya terjadi.

Sekarang, mari kita hapus tabel, dan INSERT 2k baris ke tabel dengan klausa ORDER BY.

TRUNCATE TABLE #Test;
GO

INSERT INTO #Test
SELECT number
  FROM #Sequence
 ORDER BY number

Paket eksekusi aktual untuk permintaan ini adalah sebagai berikut.

INSERT INTO #Test  SELECT number    FROM #Sequence   ORDER BY number
  |--Clustered Index Insert(OBJECT:([tempdb].[dbo].[#Test]), SET:([tempdb].[dbo].[#Test].[id] = [tempdb].[dbo].[#Sequence].[number]))
       |--Top(ROWCOUNT est 0)
            |--Sort(ORDER BY:([tempdb].[dbo].[#Sequence].[number] ASC))
                 |--Table Scan(OBJECT:([tempdb].[dbo].[#Sequence]))

Perhatikan bahwa ini adalah rencana eksekusi yang sama yang digunakan untuk pernyataan INSERT tanpa klausa ORDER BY.

Sekarang, operasi Sort tidak selalu diperlukan, seperti yang ditunjukkan Mark Smith dalam jawaban lain (jika jumlah baris yang dimasukkan rendah), tetapi ORDER BY klausa masih berlebihan dalam kasus itu, karena bahkan dengan ORDER BY eksplisit, tidak ada operasi Sort dihasilkan oleh prosesor permintaan.

Anda dapat mengoptimalkan pernyataan INSERT ke dalam tabel dengan indeks berkerumun, dengan menggunakan INSERT yang dicatat minimal, tetapi itu di luar ruang lingkup untuk pertanyaan ini.

Diperbarui 2011-11-02: Seperti yang ditunjukkan oleh Mark Smith , INSERTs ke dalam tabel dengan sebuah cluster indeks mungkin tidak selalu perlu diurutkan - klausa ORDER BY juga mubazir dalam kasus itu.

8
gonsalu