Varsa SQL Server ekleme en iyi uygulama


152

Takım üyelerinin isimlerini ve sıralarını bir yandan Competitionstutan bir sonuç tablosum var .

Öte yandan benzersiz rakip isimleri içeren bir tablo tutmam gerekiyor :

CREATE TABLE Competitors (cName nvarchar(64) primary key)

Şimdi 1. tabloda yaklaşık 200.000 sonuç var ve rakipler tablosu boş olduğunda bunu yapabilirim:

INSERT INTO Competitors SELECT DISTINCT Name FROM CompResults

Ve sorgunun yaklaşık 11.000 ad girmesi yalnızca 5 saniye sürer.

Şimdiye kadar bu kritik bir uygulama değil, bu yüzden yaklaşık 10.000 sıra ile yeni yarışma sonuçları aldığımda Rakipler tablosunu ayda bir kez kesmeyi düşünebilirim .

Ancak yeni VE mevcut rakiplerle yeni sonuçlar eklendiğinde en iyi uygulama hangisidir? Mevcut rakipler tablosunu kısaltmak istemiyorum

Yalnızca yeni rakipler için INSERT deyimi gerçekleştirmek ve varsa hiçbir şey yapmak gerekir.


70
Lütfen, yok bir hale NVARCHAR(64)sütunu, birincil (ve dolayısıyla: kümeleme) tuşuna !! Her şeyden önce - çok geniş bir anahtar - 128 bayta kadar; ve ikinci olarak değişken boyutu - tekrar: optimal değil ... Bu sahip olabileceğiniz en kötü seçim hakkında - performansınız cehennem olacak ve tablo ve dizin parçalanması her zaman% 99.9 olacaktır .....
marc_s

4
Marc'ın iyi bir noktası var. Adı pk olarak kullanmayın. Bir id, tercihen int veya hafif bir şey kullanın.
richard

6
Kimberly Tripp'in iyi bir kümeleme anahtarını neyin oluşturduğu hakkındaki blog yayınına bakın : benzersiz, dar, statik, sürekli artan. Sizin cName(o, muhtemelen statik değildir, dar değil ve sürekli artan değil kesinlikle var) .... üç dört kategoride başarısız
Marc_s

TÜM sorguların adında olacağı bir Rakip Adı tablosuna bir INT birincil anahtar ekleme noktasını göremiyorum, 'NEREDE adı'% xxxxx% 'gibi NEREDE, bu yüzden her zaman adda benzersiz bir dizine ihtiyacım var. Ama evet, ben değişken uzunluk yapma DEĞİL noktaya görebilirsiniz ..
Didier Levy

3
a) parçalanma ve b) diğer tablolarda yabancı anahtar ise iki kopyası olan veri bir hız göz) olan (karşılaşılmasına daha büyüktür kaçınarak
JamesRyan

Yanıtlar:


214

Anlamsal olarak "Rakipleri henüz mevcut olmayan yerlere ekle" sorusunu soruyorsunuz:

INSERT Competitors (cName)
SELECT DISTINCT Name
FROM CompResults cr
WHERE
   NOT EXISTS (SELECT * FROM Competitors c
              WHERE cr.Name = c.cName)

2
Ben SO üzerinde qquestion poz önce nefret ediyorum budur. Ama düşüncemin özü şu: Haftada bir kez isimler tablosunu sıfırdan yeniden oluşturmaya karşı ne kadar iyi olacak? (bunun sadece birkaç saniye sürdüğünü unutmayın)
Didier Levy

3
@Didier Levy: Verimlilik? Neden kesilmeli, yalnızca farklılıklar ile güncellenebildiğinizde yeniden oluşturun. Yani: BAŞLANGIÇ TRAN SIKIŞTIR KOMPOZLAR SIKIŞTIRME KOMPONT .. .. KOMUT TRAN = daha fazla iş.
gbn

@gbn - Cevabınız yerine if-else mantığını burada kullanmanın bir yolu var mı? Ben ilgili bir sorum var. Bana bu konuda yardımcı olabilir misiniz? stackoverflow.com/questions/21889843/…
Steam

53

Başka bir seçenek de Sonuçlar tablonuza mevcut rakipler Tablonuzla katılmanız ve katılımla eşleşmeyen farklı kayıtları filtreleyerek yeni rakiplerin bulunmasıdır:

INSERT Competitors (cName)
SELECT  DISTINCT cr.Name
FROM    CompResults cr left join
        Competitors c on cr.Name = c.cName
where   c.cName is null

Yeni sözdizimi MERGE ayrıca bunu yapmak için kompakt, zarif ve verimli bir yol sunar:

MERGE INTO Competitors AS Target
USING (SELECT DISTINCT Name FROM CompResults) AS Source ON Target.Name = Source.Name
WHEN NOT MATCHED THEN
    INSERT (Name) VALUES (Source.Name);

1
Bu durumda birleştirme harika, söylediklerini aynen yapıyor.
VorobeY1326

Kesinlikle bu gitmek için doğru yol olduğuna inanıyorum, SQL Server alt sorgu yaklaşımının aksine, optimize etmek için mümkün olan en iyi ipuçları verir.
Mads Nielsen

4
MERGE ifadesinin hala birçok sorunu var. Sadece google "SQL Birleştirme Sorunları" - birçok blogcu bunu uzunca tartıştı.
David Wilson

MERGE ifadesinde neden Hedef olarak var, ancak INSERT ifadesinde Hedef yok? Eşdeğerliği kavramayı zorlaştıran daha fazla farklılık vardır.
Peter

32

Neden başka kimse henüz bunu söylemedi bilmiyorum;

Normale.

Yarışmalar düzenleyen bir masanız var mı? Yarışmalar Rakiplerden mi oluşuyor? Bir veya daha fazla Müsabakada farklı bir Yarışmacı listesine ihtiyacınız var ......

Aşağıdaki tablolara sahip olmalısınız .....

CREATE TABLE Competitor (
    [CompetitorID] INT IDENTITY(1,1) PRIMARY KEY
    , [CompetitorName] NVARCHAR(255)
    )

CREATE TABLE Competition (
    [CompetitionID] INT IDENTITY(1,1) PRIMARY KEY
    , [CompetitionName] NVARCHAR(255)
    )

CREATE TABLE CompetitionCompetitors (
    [CompetitionID] INT
    , [CompetitorID] INT
    , [Score] INT

    , PRIMARY KEY (
        [CompetitionID]
        , [CompetitorID]
        )
    )

CompetitionCompetitors.CompetitionID ve CompetitorID üzerindeki kısıtlamalar ile diğer tabloları işaret ediyor.

Bu tür tablo yapısı ile - anahtarlarınızın hepsi basit INTS - modele uyacak iyi bir DOĞAL ANAHTARI görünmüyor, bu yüzden bir SURROGATE ANAHTARI burada iyi bir uyum olduğunu düşünüyorum.

Eğer buna sahipseniz, o zaman belirli bir yarışmadaki rakiplerin farklı listesini almak için aşağıdaki gibi bir sorgu verebilirsiniz:

DECLARE @CompetitionName VARCHAR(50) SET @CompetitionName = 'London Marathon'

    SELECT
        p.[CompetitorName] AS [CompetitorName]
    FROM
        Competitor AS p
    WHERE
        EXISTS (
            SELECT 1
            FROM
                CompetitionCompetitor AS cc
                JOIN Competition AS c ON c.[ID] = cc.[CompetitionID]
            WHERE
                cc.[CompetitorID] = p.[CompetitorID]
                AND cc.[CompetitionName] = @CompetitionNAme
        )

Ve eğer her yarışmanın skorunu istiyorsanız bir yarışmacı:

SELECT
    p.[CompetitorName]
    , c.[CompetitionName]
    , cc.[Score]
FROM
    Competitor AS p
    JOIN CompetitionCompetitor AS cc ON cc.[CompetitorID] = p.[CompetitorID]
    JOIN Competition AS c ON c.[ID] = cc.[CompetitionID]

Ve yeni rakiplerle yeni bir rekabete sahip olduğunuzda, rakipler tablosunda hangilerinin mevcut olduğunu kontrol etmeniz yeterlidir. Zaten mevcutlarsa, bu Rakipler için Rakip'e eklemez ve yenileri için eklemezsiniz.

Sonra yeni Rekabeti Rekabete eklersiniz ve son olarak Rekabetçi rakiplerin tüm bağlantılarını yaparsınız.


2
OP'nin şu anda tüm tablolarını önbelleğe alınmış bir sonuç elde etmek için yeniden yapılandırmaya uygun olduğu varsayılarak. Db'nizi ve uygulamanızı yeniden yazmak, sorunu belirli bir kapsamda çözmek yerine, her şey kolayca yerine oturmadığında, felaket için bir reçetedir.
Jeffrey Vest

1
Belki de OP'nin benimki gibi durumunda, veritabanını değiştirmek için her zaman erişiminiz yoktur .. VE eski bir veritabanını yeniden yazmak / normalleştirmek her zaman bütçede veya ayrılan zamanda değildir.
eaglei22

10

Tablolara birlikte katılmanız ve daha önce var olmayan benzersiz rakiplerin bir listesini almanız gerekir Competitors.

Bu benzersiz kayıtlar ekleyecektir.

INSERT Competitors (cName) 
SELECT DISTINCT Name
FROM CompResults cr LEFT JOIN Competitors c ON cr.Name = c.cName
WHERE c.Name IS NULL

Bu ekin benzersiz adların seçimini beklemeden hızlı bir şekilde yapılması gereken bir zaman gelebilir. Bu durumda, benzersiz adları geçici bir tabloya ekleyebilir ve daha sonra gerçek tablonuza eklemek için bu geçici tabloyu kullanabilirsiniz. Tüm işlemler geçici bir tabloya eklediğiniz sırada gerçekleşir, bu nedenle gerçek tablonuzu etkilemez. Ardından, tüm işlemler bittiğinde, gerçek tabloya hızlı bir ekleme yaparsınız. Hatta gerçek tabloya eklediğiniz son parçayı bir işlemin içine sarabilirim.


4

Yukarıdaki normalleştirmeyle ilgili cevaplar harika! Peki ya kendinizi benim gibi veritabanı şemasına veya yapısına dokunma izninizin olmadığı bir konumda bulursanız? Örneğin, DBA'lar 'tanrılar' ve önerilen tüm revizyonlar / dev / null?

Bu bağlamda, bu kod yığını veren yukarıdaki tüm kullanıcılar için de bu Yığın Taşması gönderimi ile cevap verildi gibi hissediyorum .

Ben altta yatan herhangi bir veritabanı tabloları değiştiremiyorum beri bana en çok yardımcı oldu nerede DEĞERLİ DEĞER EKLEME kodu yeniden gönderiyorum :

INSERT INTO #table1 (Id, guidd, TimeAdded, ExtraData)
SELECT Id, guidd, TimeAdded, ExtraData
FROM #table2
WHERE NOT EXISTS (Select Id, guidd From #table1 WHERE #table1.id = #table2.id)
-----------------------------------
MERGE #table1 as [Target]
USING  (select Id, guidd, TimeAdded, ExtraData from #table2) as [Source]
(id, guidd, TimeAdded, ExtraData)
    on [Target].id =[Source].id
WHEN NOT MATCHED THEN
    INSERT (id, guidd, TimeAdded, ExtraData)
    VALUES ([Source].id, [Source].guidd, [Source].TimeAdded, [Source].ExtraData);
------------------------------
INSERT INTO #table1 (id, guidd, TimeAdded, ExtraData)
SELECT id, guidd, TimeAdded, ExtraData from #table2
EXCEPT
SELECT id, guidd, TimeAdded, ExtraData from #table1
------------------------------
INSERT INTO #table1 (id, guidd, TimeAdded, ExtraData)
SELECT #table2.id, #table2.guidd, #table2.TimeAdded, #table2.ExtraData
FROM #table2
LEFT JOIN #table1 on #table1.id = #table2.id
WHERE #table1.id is null

Yukarıdaki kod, sahip olduğunuzdan farklı alanlar kullanır, ancak çeşitli tekniklerle genel özeti elde edersiniz.

Stack Overflow'daki orijinal yanıta göre bu kodun buradan kopyalandığını unutmayın .

Her neyse benim amacım "en iyi uygulama" genellikle kuramın yanı sıra yapabileceğiniz ve yapamayacağınız şeylere de iniyor.

  • Dizinleri / anahtarları normalleştirip oluşturabiliyorsanız - harika!
  • Değilse ve benim gibi kesmek kodlamak için tesisi var, umarım yukarıdaki yardımcı olur.

İyi şanslar!


Açık değilse, bu soruna dört farklı yaklaşımdır, bu yüzden birini seçin.
2'de nasch

3

Transact Charlie tarafından önerildiği gibi operasyonel tablolarınızı normalleştirmek iyi bir fikirdir ve zaman içinde birçok baş ağrısını ve problemi kurtaracaktır - ancak harici sistemlerle entegrasyonu destekleyen arayüz tabloları ve analitik gibi şeyleri destekleyen raporlama tabloları gibi şeyler vardır. işleme; ve bu tür tablolar mutlaka normalleştirilmemelidir - aslında, çoğu zaman onlar için çok, çok daha uygun ve performanslıdır .

Bu durumda, Transact Charlie'nin operasyonel masalarınız için önerisinin iyi olduğunu düşünüyorum.

Ancak, entegrasyon amacıyla CompetitorName üzerinde etkin birleşimleri desteklemek için CompetitorsNeti'ye CompetitorName'e bir dizin (zorunlu olarak benzersiz değil) ekleyeceğim ve dış kaynaklardan verilerin yüklenmesi) ve mix'e bir arayüz tablosu koyacağım: CompetitionResults.

RekabetSonuçları, rekabet sonuçlarınızda bulunan tüm verileri içermelidir. Bunun gibi bir arayüz tablosunun amacı, bir Excel sayfasından veya CSV dosyasından veya bu verilere sahip olduğunuz herhangi bir formdan kesip yeniden yüklemeyi mümkün olduğunca hızlı ve kolay hale getirmektir.

Bu arayüz tablosu, normalleştirilmiş operasyonel tablolar grubunun bir parçası olarak düşünülmemelidir. Ardından, henüz mevcut olmayan Rakiplere kayıt eklemek ve mevcut olanları güncellemek için (örneğin telefon numaraları veya e-posta adresleri gibi rakipler hakkında daha fazla bilgiye sahipseniz) Richard'ın önerdiği gibi CompetitionResults'a katılabilirsiniz.

Dikkat edeceğim bir şey - gerçekte, Rakip Adı, bana öyle geliyor ki, verilerinizde benzersiz olması pek olası değildir . 200.000 rakipte, örneğin 2 veya daha fazla David Smith'e sahip olabilirsiniz. Bu nedenle, rakiplerden telefon numaraları veya e-posta adresleri veya benzersiz olma olasılığı daha fazla olan bilgileri toplamanızı öneririm.

Operasyonel tablonuzda, Rakipler, bileşik veri anahtarına katkıda bulunan her veri öğesi için sadece bir sütun içermelidir; örneğin, birincil e-posta adresi için bir sütun olmalıdır. Ancak arayüz tablosunun birincil e-posta adresi için eski ve yeni değerler için bir yuvası olmalıdır , böylece eski değer Rakipler'deki kaydı aramak ve bu kısmını yeni değere güncellemek için kullanılabilir.

Bu yüzden CompetitionResults bazı "eski" ve "yeni" alanlara sahip olmalıdır - oldEmail, newEmail, oldPhone, newPhone, vb.

Daha sonra bazı rekabet sonuçlarınız olduğunda, CompetitionResults tablonuzu excel sayfanızdan veya sahip olduğunuz her şeyden kesebilir ve yeniden yükleyebilir ve tüm yeni rakipleri Rakipler tablosuna eklemek için tek ve verimli bir ekleme ve güncellemek için tek, verimli güncelleme çalıştırabilirsiniz. CompetitionResults'tan mevcut rakiplerle ilgili tüm bilgiler. Ayrıca, CompetitionCompetitors tablosuna yeni satırlar eklemek için tek bir ekleme yapabilirsiniz. Bunlar, CompetitionResults tablosu yüklendikten sonra yürütülebilen bir ProcessCompetitionResults saklı yordamında yapılabilir.

Bu, Oracle Applications, SAP, PeopleSoft ve diğer kurumsal yazılım paketlerinin bir çamaşır listesi ile gerçek dünyada defalarca yaptığımın ilkel bir açıklamasıdır.

Yapacağım son bir yorum daha önce SO üzerinde yaptığım bir yorum: Rakipler tablosunda Rekabetçi rakiplere bir satır eklemeden önce bir Rakip'in Rakipler tablosunda var olmasını sağlayan yabancı bir anahtar oluşturursanız , yabancı anahtar güncellemeleri ve silmeleri kademelendirir . Bu şekilde bir rakibi silmeniz gerekiyorsa, bunu yapabilirsiniz; bu rakiple ilişkili tüm satırlar otomatik olarak silinir. Aksi takdirde, varsayılan olarak yabancı anahtar, bir Rakip'i silmenize izin vermeden önce, ilgili tüm satırları CompetitionCompetitors'dan silmenizi gerektirir.

(Bazı insanlar basamaklı olmayan yabancı anahtarların iyi bir güvenlik önlemi olduğunu düşünürler, ancak deneyimlerim, popoda sadece bir gözetimin sonucu olmaktan daha sık olmayan ve bir sürü iş yapmaktan korkan bir acı olduklarıdır. Yanlışlıkla bir şeyleri silen insanlarla uğraşmak, neden "emin misiniz?" iletişim kutuları ve çeşitli düzenli yedekleme ve yedekli veri kaynakları gibi şeylere sahip olmanızdır. Verileri tamamen olan bir rakibi silmek istemek çok daha yaygındır. örneğin, yanlışlıkla birini silmek ve sonra "Ah hayır! Bunu yapmak istemedim! Ve şimdi onların rekabet sonuçları yok! Aaaahh!" , bunun için hazırlıklı olmanız gerekir, ancak birincisi çok daha yaygındır,bu nedenle, eski imo için hazırlanmanın en kolay ve en iyi yolu, yabancı anahtarları art arda güncellemeler ve silmeler yapmaktır.)


1

Tamam, bu 7 yıl önce soruldu, ama bence buradaki en iyi çözüm yeni tablodan tamamen vazgeçmek ve bunu özel bir görünüm olarak yapmak. Bu şekilde verileri çoğaltmazsınız, benzersiz veriler hakkında endişe duymazsınız ve gerçek veritabanı yapısına dokunmaz. Bunun gibi bir şey:

CREATE VIEW vw_competitions
  AS
  SELECT
   Id int
   CompetitionName nvarchar(75)
   CompetitionType nvarchar(50)
   OtherField1 int
   OtherField2 nvarchar(64)  --add the fields you want viewed from the Competition table
  FROM Competitions
GO

Diğer tablolar, WHERE yan tümceleri vb. Birleştirmeler gibi buraya başka öğeler de eklenebilir.

SELECT *
FROM vw_competitions

... ve görünüm sorgusuna herhangi bir WHERE, IN veya EXISTS yan tümcesi ekleyin.

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.