Bidang terhitung SQL dalam klausa SELECT dan GROUP BY

11

Seringkali dalam query database MS SQL Server saya, saya perlu membuat bidang terhitung, seperti ini

(CASE WHEN A.type = 'Workover' THEN 'Workover' 
      ELSE (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' 
                 WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' 
                 WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' 
                 ELSE 'Other' 
            END)
END)

dan kemudian saya perlu mengelompokkan hasil saya dengan bidang yang dihitung ini (antara lain). Karenanya, saya memiliki perhitungan yang sama dalam klausa SELECT dan GROUP BY. Apakah SQL server benar-benar melakukan perhitungan ini dua kali, atau cukup pintar hanya melakukannya sekali?

Drew
sumber

Jawaban:

13

Saya memiliki perhitungan yang sama di kedua klausa SELECT dan GROUP BY. Apakah SQL server benar-benar melakukan perhitungan ini dua kali, atau cukup pintar hanya melakukannya sekali?

Jawaban sederhana adalah bahwa SQL Server tidak memberikan jaminan umum tentang kapan, dan berapa kali, ekspresi skalar akan dievaluasi pada waktu eksekusi.

Ada segala macam perilaku rumit (dan tidak berdokumen) dalam optimizer dan mesin eksekusi terkait penempatan, pelaksanaan, dan caching ekspresi skalar. Books Online tidak banyak bicara tentang ini, tetapi yang dikatakannya adalah ini:

Hitung deskripsi skalar

Ini menggambarkan salah satu perilaku yang saya singgung sebelumnya, menunda eksekusi ekspresi. Saya menulis tentang beberapa perilaku terkini lainnya (yang dapat berubah sewaktu-waktu) di posting blog ini .

Pertimbangan lain adalah bahwa model biaya yang digunakan oleh pengoptimal kueri saat ini tidak berbuat banyak dalam hal estimasi biaya untuk ekspresi skalar. Tanpa kerangka biaya yang kuat, hasil saat ini didasarkan pada heuristik yang luas atau peluang murni.

Untuk ekspresi yang sangat sederhana, mungkin tidak banyak bedanya apakah ekspresi tersebut dievaluasi satu atau beberapa kali dalam banyak kasus. Yang mengatakan, saya telah menemukan pertanyaan besar di mana kinerja telah berdampak buruk ketika ekspresi dievaluasi secara berlebihan sejumlah besar kali, atau evaluasi terjadi pada satu utas di mana akan menguntungkan untuk mengevaluasi dalam cabang paralel dari eksekusi rencana.

Singkatnya, perilaku saat ini tidak terdefinisi, dan tidak ada banyak rencana eksekusi untuk membantu Anda mengetahui apa yang terjadi (dan itu tidak selalu nyaman untuk melampirkan debugger untuk memeriksa perilaku mesin yang terperinci, seperti dalam posting blog).

Jika Anda menjumpai kasus-kasus di mana masalah evaluasi skalar berpengaruh terhadap kinerja, sampaikan masalah dengan Dukungan Microsoft. Ini adalah cara terbaik untuk memberikan umpan balik untuk meningkatkan versi produk di masa depan.

Paul White 9
sumber
3

Seperti komentar pada pertanyaan Anda menyatakan, jawabannya adalah (setidaknya dalam pengalaman saya) "ya". SQL Server umumnya cukup pintar untuk menghindari komputasi ulang. Anda mungkin dapat memverifikasi ini dengan menunjukkan rencana eksekusi dari dalam SQL Server Management Studio. Setiap bidang terhitung ditunjuk Exprxxxxx(di mana xxxxx adalah angka). Jika Anda tahu apa yang harus dicari, Anda harus dapat memverifikasi bahwa itu menggunakan ekspresi yang sama.

Untuk menambah diskusi, opsi estetika Anda yang lain adalah ekspresi tabel umum :

with [cte] as
(
    select
        (case when a.type = 'workover' then 'workover' else 
        (case when substring(c.category, 2, 1) = 'd' then 'drilling'
              when substring(c.category, 2, 1) = 'c' then 'completion'
              when substring(c.category, 2, 1) = 'w' then 'workover'
              else 'other' end)
         end)) as [group_key],
         *
    from
        [some_table]
)
select
    [group_key],
    count(*) as [count]
from
    [cte]
group by
    [group_key]

Jawaban singkat, mereka secara fungsional identik dengan tampilan, tetapi hanya valid untuk digunakan dalam pernyataan berikutnya. Saya melihatnya sebagian besar sebagai alternatif yang lebih mudah dibaca untuk tabel turunan karena menghindari bersarang.

Meskipun tidak relevan dengan pertanyaan ini, mereka dapat mereferensikan diri mereka sendiri dan dengan cara itu digunakan untuk membangun kueri rekursif.

Joe Smith Cepat
sumber
@Cepat Joe Smith: Saya pikir Anda benar tentang Exprxxxxx, karena saya juga pernah melihatnya. Namun, jika saya memberikan nama ke ekspresi secara manual (case ... end) sebagai OpType, kemudian gunakan bidang OpType dalam klausa GROUP BY, saya mendapatkan kesalahan bahwa itu adalah nama kolom yang tidak valid.
Dr. Drew
Sayangnya, seringkali satu-satunya jalan keluar Anda untuk menentukan ekspresi dua kali adalah dengan menggunakan salah satu metode di atas: CTE, view, atau query bersarang.
Cepat Joe Smith
2
Kecuali Anda juga tahu tentang CROSS BERLAKU .
Andriy M
Menggunakan cross applydalam kasus ini adalah sedikit peregangan, dan itu akan sangat mungkin merusak kinerja dengan memperkenalkan self-join yang tidak perlu.
Cepat Joe Smith
2
Saya tidak berpikir Anda "mendapat" saran. The CROSS APPLYhanya mendefinisikan alias dari kolom dalam baris yang sama. Tidak perlu bergabung. mis.SELECT COUNT(*), hilo FROM master..spt_values CROSS APPLY (VALUES(high + low)) V(hilo) GROUP BY hilo
Martin Smith
1

Kinerja hanyalah satu aspek. Yang lainnya adalah rawatan.

Secara pribadi, saya cenderung melakukan hal berikut:

SELECT T.GroupingKey, SUM(T.value)
FROM
(
    SELECT 
        A.*
        (CASE WHEN A.type = 'Workover' THEN 'Workover' ELSE 
        (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' ELSE 'Other' END)
        END) AS GroupingKey
    FROM Table AS A
) AS T

GROUP BY T.GroupingKey

MEMPERBARUI:

Jika Anda tidak suka bersarang, Anda bisa membuat LIHAT untuk setiap tabel tempat Anda perlu menggunakan ekspresi kompleks.

CREATE VIEW TableExtended
AS 
SELECT 
    A.*
    (CASE WHEN A.type = 'Workover' THEN 'Workover' ELSE 
    (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' ELSE 'Other' END)
    END) AS GroupingKey
FROM Table AS A

Kemudian Anda bisa memilih tanpa melakukan ekstra bersarang;

SELECT GroupingKey, SUM(value)
FROM TableExtended
GROUP BY GroupingKey
Kaspars Ozols
sumber