Set tabanlı algoritma / UDF nasıl uygulanır?


13

800K satır ve 38 sütun içeren bir tablodaki her satıra karşı çalıştırmak için gereken bir algoritma var. Algoritma VBA'da uygulanır ve diğer sütunları değiştirmek için bazı sütunlardaki değerleri kullanarak bir grup matematik yapar.

Şu anda SQL sorgulamak için Excel (ADO) kullanıyorum ve VBA kullanarak istemci tarafı imleçler algoritması ile her satır arasında döngü uygulamak için. Çalışır, ancak çalışması 7 saat sürer.

VBA kodu, T-SQL içine yeniden kodlamak için çok fazla iş olacak kadar karmaşıktır.

Mümkün olduğunca CLR entegrasyonu ve UDF'leri okudum. Ayrıca veritabanına yaklaşmak için bir SSIS komut dosyası görev VBA kodu koymak düşündüm ama eminim bu tür bir performans sorunu için uzman bir metodoloji var.

İdeal olarak algoritmayı paralel kümeye dayalı bir şekilde mümkün olduğunca çok satıra (tümü?) Karşı çalıştırabilirdim.

Herhangi bir yardım, bu tür bir sorunla en iyi performansı nasıl elde edeceğinize büyük önem verdi.

--Düzenle

Yorumlar için teşekkürler, MS SQL 2014 Enterprise kullanıyorum, işte bazı ayrıntılar:

Algoritma, zaman serisi verilerinde karakteristik paternler bulur. Algoritma içindeki fonksiyonlar, polinom yumuşatma, pencereleme gerçekleştirir ve girdi kriterlerine göre ilgili bölgeleri bulur, bir düzine değer ve bazı Boole sonuçları döndürür.

Sorum gerçek algoritmadan ziyade metodoloji hakkında: Bir kerede birçok satırda paralel hesaplama elde etmek istiyorsam seçeneklerim nelerdir.

T-SQL içine yeniden kodlama görüyorum ki bu çok iş ama mümkün, ancak algoritma geliştiricisi VBA'da çalışıyor ve sık sık değişiyor, bu yüzden T-SQL sürümüyle senkronize kalmam ve her onaylamam gerekiyor değişiklik.

T-SQL set tabanlı fonksiyonları uygulamanın tek yolu mudur?


3
SSIS, veri akışınızı iyi tasarladığınızı varsayarak bazı yerel paralellikler sunabilir. Bu, satır satır hesaplaması yapmanız gerektiğinden, aradığınız görev budur. Ancak, bize özellikler (şema, ilgili hesaplamalar ve bu hesaplamaların neyi başarmayı umduğu) vermedikçe, optimize etmenize yardımcı olmanız imkansızdır. Montajda bir şeyler yazmanın en hızlı kodu sağlayabileceğini söylüyorlar, ancak benim gibi, korkunç bir şekilde
emersen

2
Her satırı bağımsız olarak işlerseniz, 800K satırı Ntoplu olarak bölebilir ve Nalgoritmanızın örneklerini Nayrı işlemcilerde / bilgisayarlarda çalıştırabilirsiniz. Öte yandan, ana darboğazınız nedir - verileri SQL Server'dan Excel'e veya gerçek hesaplamalara aktarmak? VBA işlevini hemen bir kukla sonuç döndürecek şekilde değiştirirseniz, tüm süreç ne kadar sürer? Hala saatler sürüyorsa, darboğaz veri aktarımındadır. Saniyeler alırsa, hesaplamaları yapan VBA kodunu optimize etmeniz gerekir.
Vladimir Baranov

Saklı yordam olarak çağrılan filtredir: SELECT AVG([AD_Sensor_Data]) OVER (ORDER BY [RowID] ROWS BETWEEN 5 PRECEDING AND 5 FOLLOWING) as 'AD_Sensor_Data' FROM [AD_Points] WHERE [FileID] = @FileID ORDER BY [RowID] ASC Management Studio'da satırların her biri için çağrılan bu işlev 50mS alır
medwar19

1
Yani 50 ms süren ve 800000 kez (11 saat) yürüten sorgu zaman alan şeydir. @FileID her satır için benzersiz mi, yoksa sorguyu yürütmek için gereken sayısını en aza indirgemeniz için yinelenen var mı? Ayrıca, tüm dosya kimlikleri için yuvarlanan ortalamaları tek seferde bir hazırlama tablosuna önceden hesaplayabilir (FileID'de bölümü kullanın) ve ardından her satır için bir pencere işlevine gerek kalmadan bu tabloyu sorgulayabilirsiniz. Hazırlama tablosu için en iyi kurulum, kümelenmiş bir dizin açık olması gerektiği gibi görünüyor (FileID, RowID).
Mikael Eriksson

1
En iyisi, bir şekilde her satır için db'ye dokunma ihtiyacını kaldırabileceğiniz olurdu. Bu, ya TSQL'e gitmeniz ve muhtemelen yuvarlanan avg sorgusuna katılmanız veya her satır için yeterli bilgi almanız gerektiği anlamına gelir, böylece algoritmanın ihtiyaç duyduğu her şey hemen oradadır, belki de birden fazla alt satır varsa (xml) bir şekilde kodlanır .
Mikael Eriksson

Yanıtlar:


8

Metodoloji ile ilgili olarak, yanlış b-ağacı ;-) havlıyor olduğuna inanıyorum.

Ne biliyoruz:

İlk olarak, durum hakkında bildiklerimizi birleştirip gözden geçirelim:

  • Biraz karmaşık hesaplamaların yapılması gerekiyor:
    • Bunun, tablonun her satırında olması gerekir.
    • Algoritma sık sık değişir.
    • Algoritma ... diğer sütunları değiştirmek için bazı sütunlardaki değerleri [kullanır]
    • Geçerli işlem süresi: 7 saat
  • Tablo:
    • 800.000 satır içerir.
    • 38 sütunu vardır.
  • Uygulama arka ucu:
  • Veritabanı SQL Server 2014, Enterprise Edition'dır.
  • Her satır için çağrılan bir Saklı Yordam vardır:

    • Bu, çalıştırmak için 50 ms (ortalamada sanırım) sürer.
    • Yaklaşık 4000 satır döndürür.
    • Tanım (en azından kısmen):

      SELECT AVG([AD_Sensor_Data])
                 OVER (ORDER BY [RowID] ROWS BETWEEN 5 PRECEDING AND 5 FOLLOWING)
                 as 'AD_Sensor_Data'
      FROM   [AD_Points]
      WHERE  [FileID] = @FileID
      ORDER BY [RowID] ASC
      

Ne tahmin edebiliriz:

Daha sonra, bir veya daha fazla şişe boynu bulmamıza ve bir çözüme işaret etmemize veya en azından bazı olası çözümleri ortadan kaldırmamıza yardımcı olacak ek ayrıntıları sentezleyip sentezleyemeyeceğimizi görmek için tüm bu veri noktalarına birlikte bakabiliriz.

Yorumlardaki mevcut düşünce yönü, asıl sorunun SQL Server ve Excel arasında veri aktarımı olmasıdır. Durum gerçekten bu mu? Saklı Yordam 800.000 satırın her biri için çağrılırsa ve her arama için 50 ms sürerse (yani her satır için), bu 40.000 saniyeye (ms değil) kadar ekler. Ve bu 666 dakikaya eşittir (hhmm ;-) veya 11 saatin biraz üzerinde. Yine de tüm sürecin çalışması sadece 7 saat sürdü. Zaten toplam süre içinde 4 saat var ve hatta hesaplamaları yapmak veya sonuçları tekrar SQL Server'a kaydetmek için zaman ekledik. Yani burada bir şey yok.

Saklı Yordamın tanımına bakıldığında, yalnızca bir girdi parametresi vardır @FileID; üzerinde filtre yok @RowID. Bu yüzden aşağıdaki iki senaryodan birinin gerçekleştiğinden şüpheleniyorum:

  • Bu saklı yordam yok değil aslında ama onun yerine her başı, her satır başına denilen olsun @FileIDyaklaşık 4000 satır span görünen. Döndürülen 4000 satır oldukça tutarlı bir tutarsa, 800.000 satırda bu gruplardan yalnızca 200 tanesi vardır. Ve her biri 50 ms süren 200 infaz, 7 saatin sadece 10 saniyesine denk geliyor.
  • Bu saklı yordam aslında her satır için çağrılırsa, yeni bir satır ilk kez arandığında @FileIDTampon Havuzuna yeni satırları çekmek biraz daha uzun sürmez, ancak sonraki 3999 yürütmeleri zaten zaten olduğundan daha hızlı dönecektir önbelleğe alınmış, değil mi?

Bu "filtre" saklı yordam veya SQL Server'dan Excel'e herhangi bir veri aktarımı odaklanmak kırmızı bir ringa balığı olduğunu düşünüyorum .

Şu an için, cansız performansın en alakalı göstergeleri:

  • 800.000 satır var
  • İşlem her seferinde bir satırda çalışır
  • Veri, dolayısıyla "[kullanımları] bazı sütunlarından değerleri SQL Server geri kaydediliyor diğer sütunları işlemek için [my em" faza ayrılmış ;-) dir]

Ondan şüphelendim:

  • veri toplama ve hesaplamalarda iyileştirme yapmak için bir miktar alan varken, bunları daha iyi hale getirmek işlem süresinde önemli bir düşüş anlamına gelmez.
  • en büyük darboğaz UPDATE800.000 ayrı işlem yapıyor, bu da 800.000 ayrı işlem yapıyor.

Tavsiyem (şu anda mevcut bilgilere dayanarak):

  1. En büyük iyileştirme alanınız aynı anda birden fazla satırı güncellemek olacaktır (yani bir işlemde). İşleminizi her biri FileIDyerine her biri açısından çalışacak şekilde güncellemelisiniz RowID. Yani:

    1. belirli FileIDbir dizideki 4000 satırın tamamını bir diziye okur
    2. dizi, değiştirilen alanları temsil eden öğeler içermelidir
    3. dizi boyunca dolaşarak her satırı şu anda yaptığınız gibi işleyin
    4. dizideki tüm satırlar (yani bu özel için FileID) hesaplandıktan sonra:
      1. işlem başlat
      2. her güncellemeyi her biri için ara RowID
      3. hata yoksa işlemi gerçekleştirin
      4. bir hata oluştuysa, geri al ve uygun şekilde idare et
  2. Kümelenmiş dizininiz zaten olarak tanımlanmamışsa, (FileID, RowID)bunu dikkate almalısınız (@MikaelEriksson'un Soru üzerine bir yorumda önerdiği gibi). Bu singleton UPDATE'lere yardımcı olmaz, ancak en azından bu temelli filtreleme yordamında yaptığınız gibi toplu işlemleri gerçekleştirir FileID.

  3. Mantığı derlenmiş bir dile taşımayı düşünmelisiniz. Bir .NET WinForms uygulaması veya hatta Konsol Uygulaması oluşturmanızı öneririm. SQL Agent veya Windows Zamanlanmış Görevler aracılığıyla zamanlaması kolay olduğu için Konsol Uygulamasını tercih ederim. VB.NET veya C # ile yapılması önemli değildir. VB.NET geliştiriciniz için daha doğal bir uyum olabilir, ancak yine de bazı öğrenme eğrileri olacaktır.

    Bu noktada SQLCLR'ye geçmek için bir neden göremiyorum. Algoritma sık sık değişirse, bu can sıkıcı olur Meclis her zaman yeniden dağıtmak gerekir. Bir Konsol Uygulamasını yeniden oluşturmak ve .exe'nin ağdaki uygun paylaşılan klasöre yerleştirilmesini sağlamak, böylece aynı programı çalıştırmanız ve her zaman güncel olması, yapması oldukça kolay olmalıdır.

    İşlemin tamamen T-SQL'e taşınmasının, sorundan şüphelendiğim şeyse ve sadece bir seferde bir UPDATE yapıyorsanız yardımcı olacağını düşünmüyorum.

  4. İşlem .NET'e taşınırsa, diziyi UPDATETVP tablosu değişkenine JOIN'leri çağıracak ve dolayısıyla tek bir işlem olacak bir Saklı Yordam'a geçirecek şekilde Tablo Değerli Parametreler'i (TVP'ler) kullanabilirsiniz. . TVP, INSERTtek bir işlem halinde gruplandırılmış 4000 s yapmaktan daha hızlı olmalıdır . Ancak, INSERT1 işlemde 4000 saniyenin üzerindeki TVP'lerin kullanılmasından elde edilen kazanç , 800.000 ayrı işlemden her biri 4000 satırlık yalnızca 200 işleme taşındığında görülen iyileşme kadar önemli olmayacaktır.

    TVP seçeneği VBA tarafı için yerel olarak mevcut değildir, ancak birisi test etmeye değer olabilecek bir çözüm bulmuştur:

    VBA'dan SQL Server 2008 R2'ye giderken veritabanı performansını nasıl artırabilirim?

  5. Filtre proc yalnızca kullanıyor IF FileIDiçinde WHEREmaddesi bu proc gerçekten her satır başına çağrılan IF ve o zaman ilk çalıştırma sonuçlarını önbelleğe ve başına satır kalanı için bunları kullanarak bazı işleme zamandan tasarruf edebilirsiniz FileID, sağ?

  6. Eğer işlem halletmek kez FileId başına , o zaman biz paralel işlem hakkında konuşmaya başlayabilirsiniz. Ama bu noktada gerekli olmayabilir :). Oldukça büyük 3 ideal olmayan parça ile uğraştığınız göz önüne alındığında: Excel, VBA ve 800k işlemleri, herhangi bir SSIS konuşması veya paralelkenar veya kim bilir, at öncesi öncesi optimizasyon / araba . Bu 7 saatlik işlemi 10 dakika veya daha kısa bir süreye indirebilirsek, daha hızlı hale getirmenin ek yollarını düşünmeye devam eder misiniz? Aklınızda bulunduğunuz bir hedef tamamlama süresi var mı? İşlem her bir FileID'de yapıldıktan sonra unutmayın temel olarak, bir VB.NET Konsol Uygulamanız (yani komut satırı .EXE) olsaydı, SQL Agent CmdExec adımı veya Windows Zamanlanmış Görevleri aracılığıyla, bir kerede bu FileID'lerden birkaçını çalıştırmanızı engelleyen hiçbir şey olmazdı, vb.

VE, her zaman "aşamalı" bir yaklaşım benimseyebilir ve aynı anda birkaç iyileştirme yapabilirsiniz. Örneğin FileID, grup başına güncellemeler yapmaya başlamak ve dolayısıyla bu grup için bir işlem kullanmak gibi. Ardından, TVP'nin çalışıp çalışmadığını görün. Daha sonra bu kodu alıp VB.NET'e taşımaya bakın (ve TVP'ler .NET'te çalışır, böylece güzel bir şekilde bağlantı kuracaktır).


Bilmediklerimiz hala yardımcı olabilir:

  • "Filtre" Prosedür çalıştırmak Saklanan mu SatırKimliği başına veya FileId başına ? Bu Kayıtlı Prosedürün tam tanımına bile sahip miyiz?
  • Tablonun tam şeması. Bu masa ne kadar geniş? Kaç tane değişken uzunluk alanı var? Kaç alan NULLable? NULLable varsa, kaç tanesi NULL içerir?
  • Bu tablo için dizinler. Bölümlenmiş mi? SATIR veya SAYFA Sıkıştırma kullanılıyor mu?
  • Bu tablo MB / GB cinsinden ne kadar büyük?
  • Bu tablo için dizin bakımı nasıl ele alınır? Endeksler ne kadar parçalanmış? İstatistikler bugüne kadar nasıl güncelleniyor?
  • Bu 7 saatlik süreç devam ederken başka süreçler bu tabloya yazıyor mu? Olası çekişme kaynağı.
  • Bu 7 saatlik süreç devam ederken bu tablodan başka süreçler okunuyor mu? Olası çekişme kaynağı.

GÜNCELLEME 1:

** Ne VBA (Uygulamalar için Visual Basic) ve onunla neler yapılabileceği konusunda bir karışıklık var gibi görünüyor, bu yüzden bu hepimizin aynı web sayfasında olduğumuzdan emin olmak için:


GÜNCELLEME 2:

Dikkate alınması gereken bir nokta daha var: Bağlantılar nasıl ele alınıyor? VBA kodu her işlem için Bağlantıyı açar ve kapatır mı, yoksa işlemin başlangıcında bağlantıyı açar ve işlemin sonunda kapatır mı (yani 7 saat sonra)? Bağlantı havuzu oluşturmada bile (varsayılan olarak ADO için etkinleştirilmesi gerekir), 800,200 veya 1,600,000 kez açma ve kapama yerine bir kez açılma ve kapanma arasında hala bir etki olmalıdır. Bu değerler en az 800.000 GÜNCELLEME artı 200 veya 800k EXEC'leri temel alır (filtre saklı yordamın gerçekte ne kadar yürütüldüğüne bağlı olarak).

Çok fazla bağlantı içeren bu sorun, yukarıda özetlediğim öneri ile otomatik olarak azaltılır. Bir işlem oluşturarak ve bu işlem dahilindeki tüm UPDATE'leri yaparak, bu bağlantıyı açık tutacak ve her biri için yeniden kullanacaksınız UPDATE. Belirtilen kişi başına 4000 satırı almak için bağlantının ilk çağrıdan açık tutulması FileIDya da bu "get" işleminden sonra kapatılıp UPDATE'ler için tekrar açılmasa da, şimdi bir farktan bahsettiğimizden çok daha az etkilidir. Tüm süreç boyunca 200 veya 400 toplam bağlantı.

GÜNCELLEME 3:

Bazı hızlı testler yaptım. Lütfen bunun aynı işlem değil, oldukça küçük ölçekli bir test olduğunu unutmayın (saf INSERT vs EXEC + UPDATE). Bununla birlikte, bağlantıların ve işlemlerin nasıl ele alındığına ilişkin zamanlamadaki farklılıklar hala geçerlidir, dolayısıyla buradaki bilgiler nispeten benzer bir etkiye sahip olacak şekilde tahmin edilebilir.

Test Parametreleri:

  • SQL Server 2012 Developer Edition (64 bit), SP2
  • Tablo:

     CREATE TABLE dbo.ManyInserts
     (
        RowID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
        InsertTime DATETIME NOT NULL DEFAULT (GETDATE()),
        SomeValue BIGINT NULL
     );
  • Operasyon:

    INSERT INTO dbo.ManyInserts (SomeValue) VALUES ({LoopIndex * 12});
  • Her test başına toplam kesici uçlar: 10.000
  • Her test başına TRUNCATE TABLE dbo.ManyInserts;sıfırlama : (bu testin niteliği göz önüne alındığında, FREEPROCCACHE, FREESYSTEMCACHE ve DROPCLEANBUFFERS çok fazla değer katmadı.)
  • Kurtarma Modeli: BASİT (ve Günlük dosyasında belki 1 GB ücretsiz)
  • İşlemler kullanan sınamalar, kaç İşlemden bağımsız olarak yalnızca tek bir Bağlantı kullanır.

Sonuçlar:

Test                                   Milliseconds
-------                                ------------
10k INSERTs across 10k Connections     3968 - 4163
10k INSERTs across 1 Connection        3466 - 3654
10k INSERTs across 1 Transaction       1074 - 1086
10k INSERTs across 10 Transactions     1095 - 1169

Gördüğünüz gibi, DB'ye ADO bağlantısı zaten tüm işlemlerde paylaşılsa bile, bunları açık bir işlem kullanarak (ADO nesnesi bunu işleyebilmelidir) önemli ölçüde (yani 2 katın üzerinde iyileştirme) kullanarak gruplara ayırma toplam işlem süresini azaltır.


Srutzky'nin önerdiğine hoş bir "orta adam" yaklaşımı var ve bu, SQL Server'dan ihtiyacınız olan verileri almak için PowerShell'i kullanmak, verileri çalıştırmak için VBA komut dosyanızı çağırmak ve ardından SQL Server'da bir güncelleştirme SP'yi çağırmaktır. , anahtarları ve güncellenmiş değerleri SQL sunucusuna iletir. Bu şekilde, kümeye dayalı bir yaklaşımı zaten sahip olduğunuzla birleştirirsiniz.
Steve Mangiameli

@SteveMangiameli Merhaba Steve ve yorum için teşekkürler. Ben daha erken cevap olurdu ama hasta olurdu. Fikrinin önerdiğimden ne kadar farklı olduğunu merak ediyorum. Tüm göstergeler, Excel'in VBA'yı çalıştırmak için hala gerekli olduğudur. Yoksa PowerShell'in ADO'nun yerini alacağını mı ve G / Ç'de çok daha hızlı olursa, yalnızca G / Ç'yi değiştirseniz bile buna değeceğini mi düşünüyorsunuz?
Solomon Rutzky

1
Endişeye gerek yok, daha iyi hissetmene sevindim. Daha iyi olacağını bilmiyorum. Neyi bilmediğimizi bilmiyoruz ve harika bir analiz yaptınız, ancak yine de bazı varsayımlar yapmak zorundasınız. G / Ç, kendi başına değiştirilecek kadar önemli olabilir; sadece bilmiyoruz. Sadece önerdiklerinize yardımcı olabilecek başka bir yaklaşım sunmak istedim.
Steve Mangiameli

@SteveMangiameli Teşekkürler. Ve bunu açıkladığınız için teşekkür ederim. Tam yönünüzden emin değildim ve bunu varsaymamak için en iyisini anladım. Evet, daha fazla seçeneğe sahip olmanın daha iyi olduğunu kabul ediyorum, çünkü hangi değişikliklerin yapılabileceği konusunda ne gibi kısıtlamalar olduğunu bilmiyoruz :).
Solomon Rutzky

Hey srutzky, detaylı düşünceler için teşekkürler! SQL tarafında optimize edilmiş dizinleri ve sorguları alıyorum ve darboğazları bulmaya çalışıyorum. Şu anda uygun bir sunucuya yatırım yaptım, 36 çekirdekli, 1 TB, IO bozulurken PCIe SSD'leri çıkardı. Şimdi VB kodu doğrudan paralel yürütmeler için birden çok iş parçacığı açmak gibi görünen SSIS içinde çağırmak üzerine.
medwar19

2

IMHO ve VBA altının SQL'e yeniden kodlanmasının mümkün olmadığı varsayımından yola çıkarak, VBA komut dosyasının Excel dosyasında değerlendirmeyi bitirmesine ve ardından sonuçları SSIS üzerinden SQL sunucusuna geri yazmasına izin vermeyi düşündünüz mü?

VBA alt başlangıcını bitirebilir ve bir dosya sistemi nesnesindeki veya sunucudaki bir göstergeyi çevirerek (bağlantıyı sunucuya geri yazmak için zaten yapılandırdıysanız) ve ardından bu göstergeyi kontrol etmek için bir SSIS ifadesi kullanabilirsiniz. disableSSIS çözümünüzdeki belirli bir görevin özelliğini (böylece, zamanlamasının geçersiz kılınmasından endişe ediyorsanız, içe aktarma işlemi VBA altının tamamlanmasını bekler).

Ayrıca, VBA betiğini programlı olarak başlatabilirsiniz (biraz sakat, ancak workbook_open()bu özelliği geçmişte bu doğanın "ateş ve unut" görevlerini tetiklemek için kullandım ).

VB betiğinin değerlendirme süresi bir sorun olmaya başlarsa, VB geliştiricinizin kodunu SSIS çözümü içinde bir VB betiği görevine taşımaya istekli olup olmadığını görebilirsiniz - deneyimime göre Excel uygulaması çok fazla yük bu ciltte verilerle çalışma.

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.