Làm thế nào để chứng minh sự thiếu thứ tự ngầm trong cơ sở dữ liệu?


21

Gần đây tôi đã giải thích cho các đồng nghiệp về tầm quan trọng của việc có một cột để sắp xếp dữ liệu trong bảng cơ sở dữ liệu nếu cần thiết phải làm như vậy, ví dụ như đối với dữ liệu theo thứ tự thời gian. Điều này tỏ ra hơi khó khăn vì họ có thể đơn giản chạy lại truy vấn của họ dường như vô tận và nó sẽ luôn trả về cùng một tập hợp các hàng theo cùng một thứ tự.

Tôi đã nhận thấy điều này trước đây và tất cả những gì tôi thực sự có thể làm là nhấn mạnh rằng họ tin tưởng tôi và không chỉ đơn giản cho rằng một bảng cơ sở dữ liệu sẽ hoạt động giống như một tệp CSV hoặc Excel truyền thống.

Ví dụ: thực hiện truy vấn (PostgreQuery)

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');

sẽ tạo ra một bảng với một thứ tự khái niệm rõ ràng. Chọn cùng một dữ liệu theo cách đơn giản nhất sẽ là:

SELECT * FROM mytable;

Luôn cho tôi kết quả như sau:

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

Tôi có thể làm điều này nhiều lần và nó sẽ luôn trả về cho tôi cùng một dữ liệu theo cùng một thứ tự. Tuy nhiên tôi biết rằng thứ tự ngầm này có thể bị phá vỡ, tôi đã thấy nó trước đây, đặc biệt là trong các bộ dữ liệu lớn, trong đó một số giá trị ngẫu nhiên sẽ bị ném vào vị trí "sai" khi được chọn. Nhưng điều đó đã xảy ra với tôi rằng tôi không biết làm thế nào điều này xảy ra hoặc làm thế nào để tái tạo nó. Tôi cảm thấy khó khăn để có được kết quả trên Google vì truy vấn tìm kiếm có xu hướng chỉ trả lại trợ giúp chung về sắp xếp các bộ kết quả.

Vì vậy, câu hỏi của tôi về cơ bản là:

  1. Làm cách nào tôi có thể chứng minh một cách cụ thể và cụ thể rằng thứ tự trả về của các hàng từ một truy vấn không có ORDER BYcâu lệnh là không đáng tin cậy, tốt nhất là bằng cách gây ra và hiển thị sự cố của thứ tự ngầm ngay cả khi bảng trong câu hỏi không được cập nhật hoặc chỉnh sửa ?

  2. Liệu nó có tạo ra sự khác biệt nào không nếu dữ liệu chỉ được chèn một lần và sau đó không bao giờ được cập nhật nữa?

Tôi thích một câu trả lời dựa trên postgres vì ​​đó là câu trả lời tôi quen thuộc nhất nhưng tôi quan tâm nhiều hơn đến chính lý thuyết này.


6
Không bao giờ được viết hoặc cập nhật lại nữa - Tại sao đây là bảng? Âm thanh như một tập tin. Hoặc một enum. Hoặc một cái gì đó không cần phải có trong cơ sở dữ liệu. Nếu theo trình tự thời gian, không có cột ngày để đặt hàng sao? Nếu vấn đề thời gian bạn nghĩ rằng thông tin đó sẽ đủ quan trọng để có trong bảng. Dù sao, các kế hoạch có thể thay đổi do ai đó bỏ hoặc tạo một chỉ mục mới hoặc các sự kiện như thay đổi bộ nhớ, cờ theo dõi hoặc các ảnh hưởng khác. Cuộc tranh luận của họ nghe có vẻ như tôi không bao giờ thắt dây an toàn và tôi chưa bao giờ đi qua kính chắn gió của mình, vì vậy tôi sẽ tiếp tục không đeo dây an toàn của mình. Trả lời :-(
Aaron Bertrand

9
Một số vấn đề logic không thể được giải quyết về mặt kỹ thuật hoặc không có sự tham gia của HR. Nếu công ty của bạn muốn cho phép các nhà phát triển dựa vào việc tin vào voodoo và bỏ qua tài liệu, và trường hợp sử dụng của bạn thực sự bị giới hạn trong một bảng nhỏ không bao giờ được cập nhật, hãy để họ theo cách của họ và cập nhật sơ yếu lý lịch của bạn. Nó không đáng để tranh cãi.
Aaron Bertrand

1
Bạn không có cơ sở để tuyên bố "sẽ luôn luôn". Bạn chỉ có thể yêu cầu "luôn luôn", "khi tôi kiểm tra". Ngôn ngữ có một định nghĩa - đó là hợp đồng với người dùng.
philipxy

10
Tôi tò mò tại sao những đồng nghiệp của bạn lại chống lại việc thêm order bymệnh đề vào truy vấn của họ? Họ đang cố gắng tiết kiệm lưu trữ mã nguồn? hao mòn bàn phím? mất thời gian để gõ mệnh đề đáng sợ?
mustaccio

2
Tôi đã luôn nghĩ rằng các công cụ cơ sở dữ liệu nên ngẫu nhiên hoán vị một vài hàng truy vấn đầu tiên mà ngữ nghĩa không đảm bảo cho việc đặt hàng, để giúp tạo điều kiện kiểm tra.
Doug McClean

Câu trả lời:


30

Tôi thấy ba cách để cố gắng thuyết phục họ:

  1. Hãy để họ thử cùng một truy vấn nhưng với bảng lớn hơn (số lượng hàng nhiều hơn) hoặc khi bảng đang được cập nhật giữa các lần thực hiện. Hoặc các hàng mới được chèn và một số hàng cũ bị xóa. Hoặc một chỉ mục được thêm hoặc xóa giữa các lần thực hiện. Hoặc bảng được hút chân không (trong Postgres). Hoặc các chỉ mục được xây dựng lại (trong SQL Server). Hoặc bảng được thay đổi từ cụm thành một đống. Hoặc dịch vụ cơ sở dữ liệu được khởi động lại.

  2. Bạn có thể đề nghị họ chứng minh rằng các thực thi khác nhau sẽ trả về cùng một thứ tự. Họ có thể chứng minh điều đó? Họ có thể cung cấp một loạt các thử nghiệm chứng minh rằng bất kỳ truy vấn nào cũng sẽ cho kết quả theo cùng một thứ tự, bất kể nó được thực hiện bao nhiêu lần?

  3. Cung cấp tài liệu của DBMS khác nhau trong vấn đề đó. Ví dụ:

PostgreSQL :

Hàng sắp xếp

Sau khi một truy vấn đã tạo ra một bảng đầu ra (sau khi danh sách chọn đã được xử lý), nó có thể được sắp xếp tùy ý. Nếu sắp xếp không được chọn, các hàng sẽ được trả về theo thứ tự không xác định. Thứ tự thực tế trong trường hợp đó sẽ phụ thuộc vào các loại kế hoạch quét và tham gia và thứ tự trên đĩa, nhưng không được dựa vào. Một thứ tự đầu ra cụ thể chỉ có thể được đảm bảo nếu bước sắp xếp được chọn rõ ràng.

Máy chủ SQL :

SELECT- ORDER BYĐiều khoản (Giao dịch-SQL)

Sắp xếp dữ liệu được trả về bởi một truy vấn trong SQL Server. Sử dụng mệnh đề này để:

Sắp xếp tập kết quả của một truy vấn theo danh sách cột được chỉ định và, tùy ý, giới hạn các hàng được trả về một phạm vi được chỉ định. Thứ tự các hàng được trả về trong tập kết quả không được đảm bảo trừ khi một ORDER BYmệnh đề được chỉ định.

Oracle :

order_by_clause

Sử dụng ORDER BYmệnh đề để sắp xếp các hàng trả về bởi câu lệnh. Nếu không có order_by_clause, không có đảm bảo nào tồn tại rằng cùng một truy vấn được thực hiện nhiều lần sẽ truy xuất các hàng theo cùng một thứ tự.


Với các bảng rất nhỏ không được sửa đổi, bạn có thể thấy hành vi này. Đó là dự kiến. Nhưng nó cũng không được đảm bảo. Thứ tự có thể thay đổi vì bạn đã thêm một chỉ mục hoặc bạn đã sửa đổi một chỉ mục hoặc bạn đã khởi động lại cơ sở dữ liệu và có thể nhiều trường hợp khác.
ypercubeᵀᴹ

6
Nếu đơn hàng có vấn đề, thì ai đã từng chịu trách nhiệm xem xét mã của họ nên từ chối cho đến khi họ sử dụng ĐẶT HÀNG B .NG. Các nhà phát triển của DBMS (Oracle, SQL Server, Postgres) đều nói điều tương tự về bảo đảm sản phẩm của họ và những gì không (và họ được trả nhiều hơn tôi, vì vậy họ biết họ nói gì, ngoài việc xây dựng những thứ chết tiệt này nhiều thứ).
ypercubeᵀᴹ

1
Ngay cả khi thứ tự trông giống như bây giờ, có chắc chắn rằng các bảng này sẽ không bao giờ được cập nhật trong toàn bộ thời gian của phần mềm bạn đang xây dựng không? Rằng không có hàng nào sẽ được chèn, bao giờ?
ypercubeᵀᴹ

1
Có đảm bảo rằng bảng này sẽ luôn nhỏ như vậy? Có đảm bảo rằng sẽ không có thêm cột nào được thêm vào? Tôi có thể thấy hàng chục trường hợp khác nhau trong đó bảng có thể được thay đổi trong tương lai (và một số thay đổi này có thể ảnh hưởng đến thứ tự của kết quả truy vấn). Tôi đề nghị bạn yêu cầu họ trả lời tất cả những điều này. Họ có thể đảm bảo rằng không có gì như thế sẽ xảy ra? Và tại sao họ sẽ không thêm một đơn giản ORDER BY, điều này sẽ đảm bảo trật tự, bất kể bảng sẽ thay đổi như thế nào? Tại sao không có một bổ sung an toàn, mà không có hại?
ypercubeᵀᴹ

10
Các tài liệu phải là đủ. Bất cứ điều gì khác là đoán thứ hai, và ở bất kỳ giá nào, sẽ không bao giờ được coi là dứt khoát, bất kể bạn chứng minh điều gì. Nó sẽ luôn là thứ bạn đã làm và có thể giải thích, có thể là bằng chi phí của bạn, chứ không phải là thứ gì đó . Được trang bị tài liệu, gửi "bảo hành" bằng văn bản và chỉ cần tìm kiếm sự cho phép bằng văn bản để không trả lại hàng theo yêu cầu (bạn sẽ không nhận được).

19

Đây là câu chuyện thiên nga đen một lần nữa. Nếu bạn chưa thấy cái nào thì điều đó không có nghĩa là chúng không tồn tại. Hy vọng trong trường hợp của bạn, nó sẽ không dẫn đến một cuộc khủng hoảng tài chính trên toàn thế giới, chỉ đơn giản là một vài khách hàng không hài lòng.

Tài liệu Postgres nói điều này rõ ràng:

Nếu ORDER BY không được đưa ra, các hàng được trả về theo bất kỳ thứ tự nào mà hệ thống tìm thấy nhanh nhất để sản xuất.

"Hệ thống" trong trường hợp này bao gồm chính daemon postgres (bao gồm triển khai các phương thức truy cập dữ liệu của nó và trình tối ưu hóa truy vấn), hệ điều hành cơ bản, bố cục logic và vật lý của bộ lưu trữ cơ sở dữ liệu, thậm chí có thể lưu trữ CPU. Vì bạn là người dùng cơ sở dữ liệu không có quyền kiểm soát đối với ngăn xếp đó, bạn không nên dựa vào nó tiếp tục hành xử mãi mãi theo cách nó hành xử trong chính phút này.

Đồng nghiệp của bạn đang cam kết ngụy biện khái quát vội vàng . Để bác bỏ quan điểm của họ, điều đó đủ để chỉ ra rằng giả định của họ chỉ sai một lần, ví dụ như bởi dbfiddle này .


12

Hãy xem xét ví dụ sau, nơi chúng ta có ba bảng liên quan. Đơn hàng, người dùng và OrderDetails. OrderDetails được liên kết với các khóa ngoại với bảng Đơn hàng và Bảng Người dùng. Đây thực chất là một thiết lập rất điển hình cho cơ sở dữ liệu quan hệ; được cho là toàn bộ mục đích của một DBMS quan hệ .

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);

Tại đây, chúng tôi đang truy vấn bảng OrderDetails trong đó UserID là 15:

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

Đầu ra từ truy vấn trông giống như:

╔════════════════╦═════════╦════════╗
OrderDetailsID OrderID ║ UserID
╠════════════════╬═════════╬════════╣
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
╚════════════════╩═════════╩════════╝

Như bạn có thể thấy, thứ tự của đầu ra hàng không khớp với thứ tự của các hàng trong bảng OrderDetails.

Thêm một ORDER BYđảm bảo rõ ràng hàng sẽ được trả lại cho khách hàng theo thứ tự mong muốn:

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;
╔════════════════╦═════════╦════════╗
OrderDetailsID OrderID ║ UserID
╠════════════════╬═════════╬════════╣
║ 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
╚════════════════╩═════════╩════════╝

Nếu thứ tự của hàng là bắt buộc, và các kỹ sư của bạn biết trật tự đó là bắt buộc, họ chỉ nên muốn sử dụng một ORDER BYtuyên bố, vì nó có thể tiêu tốn của họ chỉ định của họ nếu có một thất bại liên quan đến trật tự không chính xác.

Một ví dụ thứ hai, có lẽ mang tính hướng dẫn hơn, sử dụng OrderDetailsbảng từ phía trên, nơi chúng tôi không tham gia bất kỳ bảng nào khác, nhưng có một yêu cầu đơn giản để tìm các hàng khớp với cả OrderID và UserID, chúng tôi thấy vấn đề.

Chúng tôi sẽ tạo một chỉ mục để hỗ trợ truy vấn, như bạn có thể làm trong cuộc sống thực nếu hiệu suất theo bất kỳ cách nào quan trọng (khi nào không?).

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

Đây là truy vấn:

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

Và kết quả:

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

Thêm một ORDER BYmệnh đề chắc chắn sẽ đảm bảo chúng ta cũng có được sự sắp xếp chính xác ở đây.

Các mô hình giả này chỉ là những ví dụ đơn giản trong đó các hàng không được đảm bảo là "theo thứ tự" mà không có ORDER BYtuyên bố rõ ràng . Có nhiều ví dụ như thế này và vì mã động cơ DBMS thay đổi khá thường xuyên, hành vi cụ thể có thể thay đổi theo thời gian.


10

Như một ví dụ thực tế, trong Postgres, thứ tự hiện thay đổi khi bạn cập nhật một hàng:

% 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)

Tôi không nghĩ rằng các quy tắc của thứ tự ngầm này hiện có được ghi lại ở bất cứ đâu, chắc chắn có thể thay đổi mà không cần thông báo và chắc chắn không phải là hành vi di động trên các công cụ DB.


được ghi lại: câu trả lời của ypercube trích dẫn tài liệu cho chúng tôi biết rằng đơn đặt hàng không được chỉ định.
Cuộc đua nhẹ nhàng với Monica

@LightnessRacesinOrbit Tôi lấy đó làm tài liệu nói rõ cho chúng tôi biết rằng nó không được ghi lại. Ý tôi là, cũng đúng là mọi thứ không có trong tài liệu đều không được chỉ định. Đó là một loại tautology. Dù sao, tôi đã chỉnh sửa một phần của câu trả lời để cụ thể hơn.
JoL

3

không chính xác là một bản demo, nhưng quá dài cho một nhận xét.

Trên các bảng lớn, một số cơ sở dữ liệu sẽ thực hiện quét song song xen kẽ:

Nếu hai truy vấn muốn quét cùng một bảng và đến gần như cùng một lúc, thì truy vấn đầu tiên có thể là một phần thông qua bảng khi lần thứ hai bắt đầu.

Truy vấn thứ hai có thể nhận các bản ghi bắt đầu từ giữa bảng (khi truy vấn đầu tiên đang hoàn thành) và sau đó nhận các bản ghi từ đầu bảng.


2

Tạo một chỉ mục cụm có thứ tự "sai". Ví dụ, cụm trên ID DESC. Điều này thường sẽ xuất thứ tự ngược lại (mặc dù điều này cũng không được đảm bảo).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.