SQL sorgularını test etmenin en iyi yolu [kapalı]


109

Sürekli olarak karmaşık SQL sorgularının hatalarla çıktığı bir sorunla karşılaştım. Esasen bu, yanlış müşterilere posta gönderilmesine ve bunun gibi diğer 'sorunlara' neden olur.

Böyle SQL sorguları oluşturma konusunda herkesin deneyimi nedir? İki haftada bir yeni veri grupları oluşturuyoruz.

İşte bazı düşüncelerim ve bunlarla ilgili sınırlamalar:

  • Test verilerinin oluşturulması Bu, tüm doğru verilere sahip olduğumuzu kanıtlasa da, üretimdeki anormalliklerin hariç tutulmasını zorunlu kılmaz. Bu, bugün yanlış kabul edilecek ancak 10 yıl önce doğru olabilecek verilerdir; belgelenmemiştir ve bu nedenle sadece veriler çıkarıldıktan sonra bunu biliyoruz.

  • Venn diyagramları ve veri haritaları oluşturma Bu, bir sorgunun tasarımını test etmenin sağlam bir yolu gibi görünse de, uygulamanın doğru olduğunu garanti etmez. Geliştiricilerin önceden planlama yapmalarını ve yazarken neler olup bittiğini düşünmelerini sağlar.

Sorunuma verebileceğiniz her türlü katkı için teşekkürler.

Yanıtlar:


164

200 satır uzunluğunda fonksiyonlara sahip bir uygulama yazmazsınız. Bu uzun işlevleri, her biri açıkça tanımlanmış tek bir sorumluluğa sahip olan daha küçük işlevlere ayırırsınız.

SQL'inizi neden böyle yazasınız?

Tıpkı işlevlerinizi ayrıştırdığınız gibi sorgularınızı ayrıştırın. Bu, onları daha kısa, daha basit, anlaşılması daha kolay, test etmesi daha kolay, yeniden düzenlemesi daha kolay hale getirir . Ve tıpkı prosedür kodunda yaptığınız gibi, aralarına "şim" ve etraflarına "sarmalayıcılar" eklemenize izin verir.

Bunu nasıl yapıyorsun? Bir sorgunun yaptığı her önemli şeyi bir görünümde yaparak Sonra oluşturmak daha ilkel fonksiyonların dışında daha karmaşık işlevleri oluşturmak gibi, bu basit görüşlerin daha karmaşık sorgular.

Ve harika olan şey, çoğu görünüm kompozisyonu için , RDBMS'nizden tamamen aynı performansı alacaksınız. (Bazıları için yapmayacaksınız; öyleyse ne? Erken optimizasyon tüm kötülüklerin köküdür. Önce doğru kodlayın, sonra gerekirse optimize edin.)

Aşağıda, karmaşık bir sorguyu ayrıştırmak için çeşitli görünümlerin kullanımına bir örnek verilmiştir.

Örnekte, her görünüm yalnızca bir dönüşüm eklediğinden, her biri hataları bulmak için bağımsız olarak test edilebilir ve testler basittir.

Örnekteki temel tablo şu şekildedir:

create table month_value( 
    eid int not null, month int, year int,  value int );

Bu tablo kusurludur, çünkü bir veriyi, mutlak bir ayı temsil etmek için ay ve yıl olmak üzere iki sütun kullanır. Yeni, hesaplanmış sütun için spesifikasyonumuz şu şekildedir:

Bunu doğrusal bir dönüşüm olarak yapacağız, öyle ki (yıl, ay) ile aynı sıralanıyor ve öyle ki herhangi bir (yıl, ay) demet için tek bir değer var ve tüm değerler ardışık:

create view cm_absolute_month as 
select *, year * 12 + month as absolute_month from month_value;

Şimdi test etmemiz gereken şey bizim spesifikasyonumuzun doğasında var, yani herhangi bir demet (yıl, ay) için bir ve sadece bir (mutlak_ay) vardır ve bu (mutlak_ay) ların ardışık olmasıdır. Bazı testler yazalım.

selectTestimiz, aşağıdaki yapıya sahip bir SQL sorgusu olacaktır : bir test adı ve bir araya getirilmiş bir durum ifadesi. Test adı sadece rastgele bir dizedir. Case ifadesi sadece case whentest ifadeleridir then 'passed' else 'failed' end.

Test ifadeleri, testin geçmesi için doğru olması gereken SQL seçimleri (alt sorgular) olacaktır.

İşte ilk testimiz:

--a select statement that catenates the test name and the case statement
select concat( 
-- the test name
'For every (year, month) there is one and only one (absolute_month): ', 
-- the case statement
   case when 
-- one or more subqueries
-- in this case, an expected value and an actual value 
-- that must be equal for the test to pass
  ( select count(distinct year, month) from month_value) 
  --expected value,
  = ( select count(distinct absolute_month) from cm_absolute_month)  
  -- actual value
  -- the then and else branches of the case statement
  then 'passed' else 'failed' end
  -- close the concat function and terminate the query 
  ); 
  -- test result.

Bu sorguyu çalıştırmak şu sonucu verir: For every (year, month) there is one and only one (absolute_month): passed

Month_value içinde yeterli test verisi olduğu sürece bu test çalışır.

Yeterli test verileri için bir test de ekleyebiliriz:

select concat( 'Sufficient and sufficiently varied month_value test data: ',
   case when 
      ( select count(distinct year, month) from month_value) > 10
  and ( select count(distinct year) from month_value) > 3
  and ... more tests 
  then 'passed' else 'failed' end );

Şimdi ardışık olduğunu test edelim:

select concat( '(absolute_month)s are consecutive: ',
case when ( select count(*) from cm_absolute_month a join cm_absolute_month b 
on (     (a.month + 1 = b.month and a.year = b.year) 
      or (a.month = 12 and b.month = 1 and a.year + 1 = b.year) )  
where a.absolute_month + 1 <> b.absolute_month ) = 0 
then 'passed' else 'failed' end );

Şimdi sadece sorgular olan testlerimizi bir dosyaya koyalım ve o betiği veritabanında çalıştıralım. Nitekim, görünüm tanımlarımızı bir komut dosyasında (veya komut dosyalarında, ilgili görünüm başına bir dosya) veritabanına karşı çalıştıracak şekilde depolarsak, her görünüm için testlerimizi aynı komut dosyasına ekleyebiliriz , böylece (re -) görünümümüzü oluşturmak, görünümün testlerini de çalıştırır. Bu şekilde, görünümleri yeniden oluşturduğumuzda ikimiz de regresyon testleri alırız ve görünüm oluşturma, üretime karşı çalıştığında, görünüm de üretimde test edilir.


27
Bu,
sql'de

1
harika sql hacks
CodeFarmer

13
Bu harika, ama neden sütunlar için tek harfli isimler ve pek okunaklı olmayan görünüm isimleri kullanılsın? SQL neden Python'dan daha az kendi kendini belgeleyen veya okunabilir olmalıdır?
snl

1
SQL / DB dünyasında hiç bakmadığım yararlı bir şey için harika bir açıklama. Ayrıca buradaki veritabanını test etme şeklini de seviyorum.
Jackstine

Bir uyarı olarak, sql görünümlerine katılan sql görünümlerinin PostgreSQL'de çok kötü performans gösterdiğini gördüm. Ancak bu tekniği M $ SQL ile etkin bir şekilde kullandım.
Ben Liyanage

6

İstediğiniz sıklıkta yeniden yükleyebileceğiniz bir test sistemi veritabanı oluşturun. Verilerinizi yükleyin veya verilerinizi oluşturun ve kaydedin. Yeniden yüklemenin kolay bir yolunu oluşturun. Geliştirme sisteminizi bu veritabanına ekleyin ve üretime geçmeden önce kodunuzu doğrulayın. Bir sorunun üretime geçmesine her izin verdiğinizde kendinizi tekmeleyin. Bilinen sorunları doğrulamak ve test paketinizi zaman içinde büyütmek için bir test paketi oluşturun.


4

DbUnit'i kontrol etmek isteyebilirsiniz , bu nedenle programlarınız için sabit bir veri kümesiyle birim testleri yazmayı deneyebilirsiniz. Bu şekilde, az ya da çok tahmin edilebilir sonuçlara sahip sorgular yazabilmelisiniz.

Yapmak isteyebileceğiniz diğer bir şey, SQL Server yürütme yığınınızın profilini çıkarmak ve tüm sorguların gerçekten doğru olup olmadığını bulmaktır, örneğin, hem doğru hem de yanlış sonuçlar döndüren tek bir sorgu kullanıyorsanız, o zaman sorgu açıkça söz konusu, ancak uygulamanız kodun farklı noktalarına farklı sorgular gönderiyorsa ne olacak?

Sorgunuzu düzeltmeye yönelik herhangi bir girişim boşuna olacaktır ... hileli sorgular yine de yanlış sonuçları tetikleyenler olabilir.


2

Re: tpdi

case when ( select count(*) from cm_abs_month a join cm_abs_month b  
on (( a.m + 1 = b.m and a.y = b.y) or (a.m = 12 and b.m = 1 and a.y + 1 = b.y) )   
where a.am + 1 <> b.am ) = 0  

Bunun yalnızca ardışık aylar için am değerlerinin ardışık olacağını kontrol ettiğini, ardışık verilerin var olup olmadığını kontrol ettiğini unutmayın (muhtemelen başlangıçta amaçladığınız şey budur). Am hesaplamanız tamamen kapalı olsa bile, kaynak verilerinizin hiçbiri ardışık değilse (örneğin sadece çift sayılı aylarınız varsa) bu her zaman geçecektir.

Ayrıca bir şey mi kaçırıyorum yoksa bu ON cümlesinin ikinci yarısı yanlış ay değerini mi çarpıyor? (yani, 12 / 2011'in 1 / 2010'dan sonra gelip gelmediğini kontrol eder)

Daha da kötüsü, doğru hatırlıyorsam, SQL Server, optimize edici sanal ellerini havaya fırlatmadan ve her istekte tam tablo taramaları yapmaya başlamadan önce en azından 10 seviyeden daha az görünüm sağlar, bu yüzden bu yaklaşımı fazla yapmayın.

Test senaryolarınızı test etmeyi unutmayın!

Aksi takdirde, olası girdi biçimlerinin çoğunu veya tamamını kapsayacak çok geniş bir veri kümesi oluşturmak, bu verilere karşı beklenen sonuçları kontrol etmeyi otomatikleştirmek için SqlUnit veya DbUnit veya başka bir * Birimi kullanmak ve gerektiğinde bunları gözden geçirmek, sürdürmek ve güncellemek genellikle gidilecek yol.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.