Bir veritabanında örtük düzen olmadığının kanıtlanması nasıl sağlanır?


21

Son zamanlarda meslektaşlarımıza, örneğin kronolojik olarak sıralanan veriler için gerekli olması durumunda, bir veritabanı tablosundaki verileri sıralamak için bir sütuna sahip olmanın önemini açıklıyordum. Bu biraz zor oldu çünkü sadece sorgularını sonsuz bir şekilde yeniden çalıştırabildiler ve daima aynı sıradaki satırları geri döndürdüler.

Bunu daha önce farkettim ve yapabileceğim tek şey bana güvendikleri konusunda ısrar ediyor ve bir veritabanı tablosunun geleneksel bir CSV veya Excel dosyası gibi davranacağını varsaymak değil.

Örneğin, (PostgreSQL) sorgusunu yürütmek

create table mytable (
    id INTEGER PRIMARY KEY,
    data TEXT
);
INSERT INTO mytable VALUES
    (0, 'a'),
    (1, 'b'),
    (2, 'c'),
    (3, 'd'),
    (4, 'e'),
    (5, 'f'),
    (6, 'g'),
    (7, 'h'),
    (8, 'i'),
    (9, 'j');

net bir kavramsal sıraya sahip bir masa yaratacaktır. Aynı veriyi en basit şekilde seçmek şöyle olacaktır:

SELECT * FROM mytable;

Bana her zaman aşağıdaki sonuçları verir:

 id | data 
----+------
  0 | a
  1 | b
  2 | c
  3 | d
  4 | e
  5 | f
  6 | g
  7 | h
  8 | i
  9 | j
(10 rows)

Bunu tekrar tekrar yapabilirim ve her zaman aynı verileri aynı sırada bana döndürür. Bununla birlikte, bu örtük düzenin bozulabileceğini biliyorum, daha önce, özellikle bazı rasgele değerlerin seçildiğinde "yanlış" yere atılacağı büyük veri setlerinde görmüştüm. Ancak, bunun nasıl olduğunu ya da nasıl çoğaltacağımı bilmediğimin farkına vardım. Arama sorgusu sonuç kümelerini sıralamak için yalnızca genel yardım döndürme eğiliminde olduğundan Google’da sonuç almayı zor buluyorum.

Yani, benim sorularım aslında bunlar:

  1. İfadesiz bir sorgudan satırların geri dönüş sırasının ORDER BY, tercihen söz konusu tablo güncellenmemiş ya da düzenlenmemiş olsa bile , dolaylı siparişin bozulmasına neden olarak ve göstererek güvenilir ve somut bir şekilde nasıl kanıtlayabilirim ?

  2. Veriler yalnızca bir kez topluca yerleştirildikten ve bir daha güncellenmediğinde, herhangi bir fark yaratır mı?

Postgres tabanlı bir cevabı tercih ederim çünkü en aşina olduğum cevap budur ama teoriye daha çok ilgi duyuyorum.


6
“Asla tekrar yazılmadı veya tekrar güncellendi” - bu neden bir masa? Bir dosya gibi geliyor. Veya bir numara. Ya da veritabanında olması gerekmeyen bir şey. Kronolojik ise, sipariş edilecek tarih sütunu yok mu? Kronoloji önemliyse, bu bilginin tabloda olması için yeterince önemli olacağını düşünürsünüz. Her neyse, yeni bir indeks oluşturan veya yeni bir dizin oluşturan veya hafıza değişiklikleri, izleme bayrakları veya diğer etkiler gibi olaylar nedeniyle planlar değişebilir. Argümanları “Emniyet kemerimi asla takmıyorum ve ön camdan asla geçmedim, bu yüzden emniyet kemerimi takmamaya devam edeceğim.” :-(
Aaron Bertrand

9
Bazı mantık problemleri teknik olarak veya İK katılımı olmadan çözülemez. Şirketiniz, voodoo'ya inanmaya ve belgeleri görmezden gelmeye çalışan geliştirici uygulamalarına izin vermek istiyorsa ve kullanım durumunuz gerçekten hiç güncellenmemiş küçük bir masa ile sınırlıysa, onların yoluna çıkmalarını ve özgeçmişinizi güncellemelerini sağlayın. Tartışmaya değmez.
Aaron Bertrand

1
"Her zaman olacak" ı iddia etmek için hiçbir dayanak yok. "Kontrol ettiğimde" yalnızca "her zaman", "olduğunu iddia edebilirsiniz. Dilin bir tanımı vardır - kullanıcıyla yapılan sözleşme budur.
philipxy

10
Ben merak ediyorum neden senin bu meslektaşları ekleyerek karşıyız order bysorguları için maddeyi? Kaynak kod deposundan tasarruf etmeye mi çalışıyorlar? klavye aşınma ve yıpranma? korkunç fıkra yazmak için ne kadar sürüyor?
mustaccio

2
Veritabanı motorlarının, semantiklerin bir siparişte garanti etmediği ilk birkaç sorgu sırasına rastgele izin vermesi gerektiğini düşündüm.
Doug McClean

Yanıtlar:


30

Onları ikna etmeye çalışmanın üç yolunu görüyorum:

  1. Aynı sorguyu denemelerine izin verin, ancak daha büyük tablolarla (daha fazla satır) veya tablo yürütmeler arasında güncellenirken. Veya yeni satırlar eklenir ve bazı eski olanlar silinir. Veya yürütmeler arasında bir dizin eklenir veya kaldırılır. Veya masa vakumlanır (Postgres'te). Veya dizinler yeniden oluşturulur (SQL Server'da). Veya tablo kümelenmiş bir yığına değiştirilir. Veya veritabanı servisi yeniden başlatılır.

  2. Farklı uygulamaların aynı sırayı geri getireceğini kanıtlamalarını önerebilirsiniz. Bunu kanıtlayabilirler mi? Herhangi bir sorgunun kaç kez çalıştırıldığına bakılmaksızın aynı sonucu vereceğini kanıtlayan bir dizi test sunabilirler mi?

  3. Bu konuda çeşitli DBMS belgelerini sağlayın. Örneğin:

PostgreSQL :

Satırları Sıralama

Bir sorgu bir çıktı tablosu oluşturduktan sonra (seçim listesi işlendikten sonra) isteğe bağlı olarak sıralanabilir. Sıralama seçilmezse, satırlar belirtilmemiş bir sırada döndürülür. Bu durumda gerçek sıra , tarama ve birleştirme planı türlerine ve diskteki sırasına bağlı olacaktır , ancak güvenilmemelidir. Belirli bir çıktı siparişi, yalnızca sıralama adımı açıkça seçilmişse garanti edilebilir.

SQL Server :

SELECT- ORDER BYCümle (Transact-SQL)

SQL Server'da bir sorgu tarafından döndürülen verileri sıralar. Bu maddeyi kullanarak:

Sorgunun sonuç kümesini belirtilen sütun listesine göre sıralayın ve isteğe bağlı olarak döndürülen satırları belirtilen bir aralığa getirin. Sonuç kümesinde satırların döndürülme sırası, bir ORDER BYmadde belirtilmediği sürece garanti edilmez .

Oracle :

order_by_clause

ORDER BYDeyimden döndürülen satırları sıralamak için yan tümce kullanın . Order_by_clause olmadan, bir defadan fazla yürütülen aynı sorgunun aynı sırada satırları alacağının garantisi yoktur.


Değiştirilmez çok küçük tablolar ile, olabilir bu davranışlar görüyorum. Bu bekleniyor. Ancak bu da garanti edilmez. Bir dizin eklediğiniz veya bir dizini değiştirdiğiniz veya veritabanını ve muhtemelen birçok başka durumu yeniden başlattığınız için sıra değişebilir.
ypercubeᵀᴹ

6
Eğer emir önemli ise, o zaman kodunu incelemekten kim sorumludur SİPARİŞ BY kullanana kadar reddetmelidir. DBMS'lerin (Oracle, SQL Server, Postgres) geliştiricileri, ürünlerinin ne garanti ettiği ve neyin ne olmadığı (ve benden çok daha fazla para aldıkları konusunda aynı şeyi söylerler, bu yüzden bu lanet şeyi inşa etmenin yanı sıra ne söylediklerini bilirler) eşyalar).
ypercubeᵀᴹ

1
Sipariş şimdi aynı görünse bile, bu tabloların oluşturduğunuz yazılımın kullanım ömrü boyunca hiçbir zaman güncellenmeyeceği kesin mi? Artık hiç satır eklenmeyecek mi?
ypercubeᵀᴹ

1
Bu masanın her zaman bu kadar küçük olacağının garantisi var mı? Başka sütun eklenmeyeceğinin garantisi var mı? Gelecekte tablonun değiştirilebileceği onlarca farklı durum görebiliyorum (ve bu değişikliklerin bazıları sorgu sonucunun sırasını etkileyebilir). Tüm bunlara cevap vermelerini istemenizi öneririm. Böyle bir şeyin asla gerçekleşmeyeceğini garanti edebilirler mi? Ve neden masayı nasıl değiştirecek olursa olsunORDER BY , niçin basit bir garanti vermeyecekler ki bu da siparişi garanti edecek ? Neden güvenli bir katma olmadı, ki bu da zarar vermez?
ypercubeᵀᴹ

10
Belgeler yeterli olmalı. Başka bir şey ikinci bir tahmindir ve herhangi bir oranda, kanıtladığınız şey ne olursa olsun, asla kesin olarak görülmeyecektir. Her zaman olacak bir şey mi yaptım muhtemelen pahasına ve açıklanabilir, yerine bir şey . Belgelerle donanmış olarak, "garantinizi" yazılı olarak gönderin ve satırları istediğiniz sırada iade etmemek için yazılı izin isteyin (alamazsınız).

19

Bu tekrar tekrar siyah kuğu hikayesi. Henüz bir tane görmediyseniz, onların var olmadığı anlamına gelmez. Umarım sizin durumunuzda, sadece birkaç mutsuz müşteriye, başka bir dünya çapında finansal krize yol açmaz.

Postgres dokümantasyonu açıkça şunu söylüyor :

SİPARİŞ TARAFINDAN verilmezse, satırlar, sistemin üretmeyi en hızlı bulduğu sırayla döndürülür.

Bu durumda "sistem" postgres arka plan programının kendisini (veri erişim yöntemlerinin ve sorgu iyileştiricisinin uygulanması dahil), temel işletim sistemini, veri tabanı depolamasının mantıksal ve fiziksel yerleşimini, hatta CPU önbelleklerini içerir. Veritabanı kullanıcısı olarak o yığının üzerinde hiçbir kontrolünüz olmadığı için, bu andaki davranış şeklini sonsuza dek sürdürmeye devam etmenize güvenmemelisiniz.

Meslektaşlarınız acele genelleme yanıltıcılığı yapıyorlar . Bu konuyu ispatlamak için , varsayımlarının yalnızca bir kez yanlış olduğunu göstermek, örneğin bu dbfiddle tarafından yeterlidir .


12

İlişkili üç tablomuz olan aşağıdaki örneği düşünün. Siparişler, Kullanıcılar ve OrderDetails. OrderDetails, yabancı anahtarlarla Siparişler tablosuna ve Kullanıcı Tablosuna bağlanır. Bu aslında ilişkisel veritabanları için çok tipik bir kurulumdur; tartışmalı bir ilişkisel DBMS'nin tüm amacı .

USE tempdb;

IF OBJECT_ID(N'dbo.OrderDetails', N'U') IS NOT NULL
DROP TABLE dbo.OrderDetails;

IF OBJECT_ID(N'dbo.Orders', N'U') IS NOT NULL
DROP TABLE dbo.Orders;

IF OBJECT_ID(N'dbo.Users', N'U') IS NOT NULL
DROP TABLE dbo.Users;

CREATE TABLE dbo.Orders
(
    OrderID int NOT NULL
        CONSTRAINT OrderTestPK
        PRIMARY KEY
        CLUSTERED
    , SomeOrderData varchar(1000)
        CONSTRAINT Orders_somedata_df
        DEFAULT (CRYPT_GEN_RANDOM(1000))
);

CREATE TABLE dbo.Users
(
    UserID int NOT NULL
        CONSTRAINT UsersPK
        PRIMARY KEY
        CLUSTERED
    , SomeUserData varchar(1000)
        CONSTRAINT Users_somedata_df
        DEFAULT (CRYPT_GEN_RANDOM(1000))
);

CREATE TABLE dbo.OrderDetails
(
    OrderDetailsID int NOT NULL
        CONSTRAINT OrderDetailsTestPK
        PRIMARY KEY
        CLUSTERED
    , OrderID int NOT NULL
        CONSTRAINT OrderDetailsOrderID
        FOREIGN KEY
        REFERENCES dbo.Orders(OrderID)
    , UserID int NOT NULL
        CONSTRAINT OrderDetailsUserID
        FOREIGN KEY
        REFERENCES dbo.Users(UserID)
    , SomeOrderDetailsData varchar(1000)
        CONSTRAINT OrderDetails_somedata_df
        DEFAULT (CRYPT_GEN_RANDOM(1000))
);

INSERT INTO dbo.Orders (OrderID)
SELECT TOP(100) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM sys.syscolumns sc;

INSERT INTO dbo.Users (UserID)
SELECT TOP(100) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM sys.syscolumns sc;

INSERT INTO dbo.OrderDetails (OrderDetailsID, OrderID, UserID)
SELECT TOP(10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
    , o.OrderID
    , u.UserID
FROM sys.syscolumns sc
    CROSS JOIN dbo.Orders o
    CROSS JOIN dbo.Users u
ORDER BY NEWID();

CREATE INDEX OrderDetailsOrderID ON dbo.OrderDetails(OrderID);
CREATE INDEX OrderDetailsUserID ON dbo.OrderDetails(UserID);

Burada, Kullanıcı Adının 15 olduğu OrderDetails tablosunu sorguluyoruz:

SELECT od.OrderDetailsID
    , o.OrderID
    , u.UserID
FROM dbo.OrderDetails od
    INNER JOIN dbo.Users u ON u.UserID = od.UserID
    INNER JOIN dbo.Orders o ON od.OrderID = o.OrderID
WHERE u.UserID = 15

Sorgudan çıkan çıktı şöyle görünür:

╔════════════════╦═════════╦════════╗
De OrderDetailsID ║ OrderID ║ Kullanıcı Kimliği ║
╠════════════════╬═════════╬════════╣
║ 2200115 ║ 2 ║ 15 ║
║ 630215 ║ 3 ║ 15 ║
║ 1990215 ║ 3 ║ 15 ║
║ 4960215 ║ 3 ║ 15 ║
║ 100715 ║ 8 ║ 15 ║
║ 3930815 ║ 9 ║ 15 ║
║ 6310815 ║ 9 ║ 15 ║
║ 4441015 ║ 11 ║ 15 ║
║ 2171315 ║ 14 ║ 15 ║
║ 3431415 ║ 15 ║ 15 ║
║ 4571415 ║ 15 ║ 15 ║
║ 6421515 ║ 16 ║ 15 ║
║ 2271715 ║ 18 ║ 15 ║
║ 2601715 ║ 18 ║ 15 ║
║ 3521715 ║ 18 ║ 15 ║
║ 221815 ║ 19 ║ 15 ║
║ 3381915 ║ 20 ║ 15 ║
║ 4471915 ║ 20 ║ 15 ║
╚════════════════╩═════════╩════════╝

Gördüğünüz gibi, satır çıktısının sırası, OrderDetails tablosundaki satırların sırasına uymuyor.

Açık bir şekilde eklemek, ORDER BYsatırların müşteriye istenen sırada geri gönderilmesini sağlar:

SELECT od.OrderDetailsID
    , o.OrderID
    , u.UserID
FROM dbo.OrderDetails od
    INNER JOIN dbo.Users u ON u.UserID = od.UserID
    INNER JOIN dbo.Orders o ON od.OrderID = o.OrderID
WHERE u.UserID = 15
ORDER BY od.OrderDetailsID;
╔════════════════╦═════════╦════════╗
De OrderDetailsID ║ OrderID ║ Kullanıcı Kimliği ║
╠════════════════╬═════════╬════════╣
║ 3915 ║ 40 ║ 15 ║
║ 100715 ║ 8 ║ 15 ║
║ 221815 ║ 19 ║ 15 ║
║ 299915 ║ 100 ║ 15 ║
║ 368215 ║ 83 ║ 15 ║
║ 603815 ║ 39 ║ 15 ║
║ 630215 ║ 3 ║ 15 ║
║ 728515 ║ 86 ║ 15 ║
║ 972215 ║ 23 ║ 15 ║
║ 992015 ║ 21 ║ 15 ║
║ 1017115 ║ 72 ║ 15 ║
║ 1113815 ║ 39 ║ 15 ║
╚════════════════╩═════════╩════════╝

Satırların sırası zorunludur, ve mühendisler bu düzen şarttır bilirlerse, sadece hiç gerektiğini istediğiniz bir kullanmak ORDER BYyanlış sipariş ile ilgili bir başarısızlık varsa o onlara atamasını mal olabilir çünkü deyimi.

Bir kullanarak ikinci, belki de daha öğretici bir örnek, OrderDetailsbiz nereye yukarıdan tablo değil başka tablo birleştirme, ancak Sipariş Kimliği ve kullanıcı kimliği hem eşleşen satırları bulmak için basit bir şartı var, biz sorun görüyoruz.

Performansı herhangi bir şekilde önemliyse, gerçek hayatta muhtemelen yapacağınız gibi, sorguyu desteklemek için bir dizin oluşturacağız (ne zaman değil?).

CREATE INDEX OrderDetailsOrderIDUserID ON dbo.OrderDetails(OrderID, UserID);

İşte sorgu:

SELECT od.OrderDetailsID
FROM dbo.OrderDetails od
WHERE od.OrderID = 15
    AND (od.UserID = 21 OR od.UserID = 22)

Ve sonuçlar:

╔════════════════╗
De OrderDetailsID ║
╠════════════════╣
║ 21421 ║
║ 5061421 ║
║ 7091421 ║
14 691422 ║
3471422
║ 7241422 ║
╚════════════════╝

Bir ORDER BYcümle eklemek kesinlikle burada doğru sıralamayı almamızı sağlayacaktır.

Bu örnekler, açık bir ORDER BYifade olmadan satırların "sıralı" olmalarının garanti edilmediği basit örneklerdir . Bunun gibi daha pek çok örnek var ve DBMS motor kodu oldukça sık değiştiğinden, belirli davranış zaman içinde değişebilir.


10

Pratik bir örnek olarak, Postgres'te bir satır güncellediğinizde sipariş şu anda değişir:

% SELECT * FROM mytable;
 id | data 
----+------
  0 | a
  1 | b
  2 | c
  3 | d
  4 | e
  5 | f
  6 | g
  7 | h
  8 | i
  9 | j
(10 rows)

% UPDATE mytable SET data = 'ff' WHERE id = 5;
UPDATE 1
% SELECT * FROM mytable;
 id | data 
----+------
  0 | a
  1 | b
  2 | c
  3 | d
  4 | e
  6 | g
  7 | h
  8 | i
  9 | j
  5 | ff
(10 rows)

Var olan bu gizli sipariş kurallarının herhangi bir yerde belgelendiğini, kesinlikle önceden haber verilmeksizin değiştirilebileceğini ve kesinlikle DB motorlarında taşınabilir davranış olmadığını düşünüyorum.


Bu edilir belgelenmiştir: ypercube cevabı sırası belirtilmemiş olduğunu bize anlatan belgelere tırnak.
Monica ile

@LightnessRacesinOrbit Belgeleri açıkça belgelenmiş olmadığını bize söylerken kabul ediyorum. Demek istediğim, belgelerde yer almayan hiçbir şeyin belirtilmemiş olduğu da doğru. Bir çeşit totoloji. Her neyse, cevabın bu kısmını daha spesifik olması için düzenledim.
Jol

3

tam olarak bir demo değil, yorum yapmak için çok uzun.

Büyük tablolarda bazı veritabanları harmanlanmış paralel taramalar yapar:

İki sorgu aynı tabloyu taramak ve hemen hemen aynı saatte ulaşmak isterse, ikincisi başladığında ilk tablonun bir parçası olabilir.

İkinci sorgu, tablonun ortasından başlayarak (ilk sorgu tamamlandığı için) kayıtları alabilir ve ardından tablonun başından itibaren kayıtları alabilir.


2

"Yanlış" sırasına sahip kümelenmiş bir dizin oluşturun. Örneğin, küme açık ID DESC. Bu, çoğu zaman tersi sırasını verir (bu da garanti edilmese de).

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.