Ana içeriğe geç
Kesit SQL
İleri · SQL Server Operasyon

Sorgu parmak izi (query fingerprint): sorguları normalleştir, en maliyetli 10 sorguyu bul

Sabit değerleri ayıklayıp aynı biçimdeki sorguları gruplamak, toplam ve ortalama maliyeti ayırmak, query_hash, kişisel veri riski.

Uygulamada aç, 9 alıştırmayı çözÜcretsiz, hesap gerekmez, kurulum yok.

Bu sayfa dersin okuma sürümüdür. Metin bir kutuyu çalıştırmanı ya da adıma geçmeni söylüyorsa dersi uygulamada aç.

Sorun: aynı sorgu binlerce farklı metin

Günlükte şu iki satır aynı sorgudur, ama metinleri farklıdır:

SELECT customer_id, full_name, segment FROM bank_customers WHERE customer_id = 412
SELECT customer_id, full_name, segment FROM bank_customers WHERE customer_id = 87

Metne göre gruplarsan her biri tek satır görünür ve pahalı olan gizlenir: tek çağrısı ucuz, bin çağrısı pahalı bir sorguyu göremezsin. Çözüm, sabitleri atıp parmak izi çıkarmaktır:

SELECT customer_id, full_name, segment FROM bank_customers WHERE customer_id = ?
Veri kurgudurops_ tabloları hayali bir portal için tohumlu üreteçle kuruldu. Gerçek bir sistemden alınmadı; süre ve okuma değerleri ölçüm değildir. Motor SQLite olduğu için günlükteki SQL Server sorguları çalıştırılmaz, metin olarak incelenir. Tabloları sorgularsın, sistem yönetimi komutlarını yalnız okursun.

Ne kadar sıkışıyor?

Günlükte 7.607 satır, 3.057 farklı metin var. Parmak izi sayısına kendin bak:

SELECT COUNT(*) AS satir,
       COUNT(DISTINCT query_text) AS farkli_metin,
       COUNT(DISTINCT REGEXP_REPLACE(REGEXP_REPLACE(query_text, '''[^'']*''', '?'), '[0-9]+', '?')) AS parmak_izi
FROM ops_query_log;

Adım adım normalleştirme

  1. Tırnaklı metinleri ? yap: '[^']*'. Önce bu adım gelir, çünkü metnin içinde rakam olabilir.
  2. Sayıları ? yap: [0-9]+.
  3. Değişken uzunluklu listeyi tek biçime indir: IN (?, ?, ?) ile IN (?, ?) aynı sorgudur.
SELECT DISTINCT REGEXP_REPLACE(REGEXP_REPLACE(query_text, '''[^'']*''', '?'), '[0-9]+', '?') AS parmak_izi
FROM ops_query_log
WHERE query_text LIKE '%IN (%';
SQL türü farkıPostgreSQL REGEXP_REPLACE için 'g' bayrağı ister, yoksa yalnız ilk eşleşmeyi değiştirir. Otomatik çeviri bunu ekler. Belgeye göre SQL Server'da REGEXP_REPLACE 2025 sürümüyle gelir, Azure SQL'de de vardır; 2022 ve öncesinde yoktur.

En maliyetli 10 sorgu

WITH n AS (SELECT REGEXP_REPLACE(REGEXP_REPLACE(query_text, '''[^'']*''', '?'), '[0-9]+', '?') AS parmak_izi, logical_reads, duration_ms FROM ops_query_log)
SELECT parmak_izi, COUNT(*) AS calisma, SUM(logical_reads) AS toplam_okuma, ROUND(AVG(duration_ms)) AS ort_ms
FROM n
GROUP BY parmak_izi
ORDER BY toplam_okuma DESC, parmak_izi
LIMIT 10;

Bu haftalık listede ilk sırayı gece ETL'i alır. Bu bir sorun mu? Kullanıcı gündüz şikâyet ediyorsa değil. Liste hangi zaman dilimi için sorusu olmadan anlamsızdır. Salı sabahı için filtrele, sonuç değişir.

Hangi "maliyet"?

ÖlçüYakaladığıKaçırdığı
Toplam okumaSunucuya en çok yük bindirenTek çağrısı yavaş ama seyrek olan
Ortalama süreKullanıcının beklediğiÇok sık çalışan ucuz sorgu; sıkışıklıkta herkes yavaşlar
Çalışma sayısıÖnbellek ya da toplu okuma adayıAz çalışan pahalı sorgu

Süre sıkışıklıktan etkilenir: suçsuz sorgu da uzar. Okuma sayısı yapılan işi gösterir. Bu yüzden önce okumaya, sonra süreye bak.

SQL Server'ın hazır parmak izi: query_hash

Normalleştirmeyi elle yapmak zorunda değilsin. Belgeye göre sys.dm_exec_query_stats içindeki query_hash, yalnız sabit değerleri farklı olan sorguların toplam kaynak kullanımını bulmak içindir. İzleme: DMV'ler dersindeki sorgu plan başına listeler; bu sorgu biçim başına toplar.

-- Yalnız okumak için
SELECT TOP 10 qs.query_hash,
       SUM(qs.execution_count) AS calisma,
       SUM(qs.total_logical_reads) AS toplam_okuma,
       MIN(t.text) AS ornek_metin
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t
GROUP BY qs.query_hash
ORDER BY toplam_okuma DESC;

Sınırlar ve kişisel veri

Alıştırmalara geç (9)