考虑以下示例,其中有三个相关表。订单,用户和OrderDetails。OrderDetails通过外键链接到Orders表和Users表。本质上,这是关系数据库的非常典型的设置。可以说,关系 DBMS 的全部目的。
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);
在这里,我们要查询UserID为15的OrderDetails表:
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
查询的输出如下:
╔════════════════╦═════════╦════════╗
║OrderDetailsID║OrderID║用户ID║
╠════════════════╬═════════╬════════╣
║2200115║2║15║
║630215║3║15║
215 1990215║3║15║
║4960215║3║15║
715 100715║8║15║
308 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║
╚════════════════牛皮═════════牛皮════════╝
如您所见,行的输出顺序与OrderDetails表中的行顺序不匹配。
添加显式ORDER BY
确保行将以所需顺序返回给客户端:
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║用户ID║
╠════════════════╬═════════╬════════╣
║3915║40║15║
715 100715║8║15║
║221815║19║15║
║299915║100║15║
║368215║83║15║
║603815║39║15║
║630215║3║15║
║728515║86║15║
║972215║23║15║
2015 992015║21║15║
║1017115║72║15║
138 1113815║39║15║
╚════════════════牛皮═════════牛皮════════╝
如果行顺序是必不可少的,并且您的工程师知道命令是必不可少的,那么他们应该只想使用一条ORDER BY
语句,因为如果发生与错误顺序有关的故障,可能会花费他们的指定时间。
第二个示例,也许是更具启发性的示例,使用OrderDetails
上面的表,其中我们没有联接任何其他表,但是有一个简单的要求,即找到与OrderID和UserID都匹配的行,我们看到了问题。
我们将创建一个索引来支持查询,就像在现实生活中,如果性能在任何方面都很重要(在什么时候不是吗?),您可能会做的那样。
CREATE INDEX OrderDetailsOrderIDUserID ON dbo.OrderDetails(OrderID, UserID);
这是查询:
SELECT od.OrderDetailsID
FROM dbo.OrderDetails od
WHERE od.OrderID = 15
AND (od.UserID = 21 OR od.UserID = 22)
结果:
╔════════════════╗
║OrderDetailsID║
╠════════════════╣
║21421║
║5061421║
║7091421║
691 691422║
║3471422║
║7241422║
╚════════════════╝
添加ORDER BY
子句绝对可以确保我们在这里也获得正确的排序。
这些模型只是简单的示例,其中如果没有明确的ORDER BY
声明,则不能保证行是“有序的” 。还有更多类似的示例,并且由于DBMS引擎代码的更改非常频繁,因此特定行为可能会随时间而改变。