sql-server - SQL Server:性能问题:WHERE 子句中的 OR 语句替换
问题描述
我只想基于列select
的表中的记录。应该在另一个名为 的表中。但是,两个表(客户端 1 和客户端 2)中当前都有 2 个,它们都有不同的.Stock
PostingDate
PostingDate
InitDate
InitClient
client
InitDate
使用下面的代码,我得到了我目前所需要的,基于下面还包含的示例数据。但是,出现了两个问题,首先是基于数百万条记录,查询花费的时间太长(几小时)。其次,每次包含新客户时,它都不是动态的。
解决性能问题的一个潜在选择是编写两个单独的查询,一个用于客户端 1,一个用于客户端 2,UNION
中间有一个。不幸的是,这不够动态,因为可能有多个客户端。
SELECT
Material
,Stock
,Stock.PostingDate
,Stock.Client
FROM Stock
LEFT JOIN (SELECT InitDate FROM InitClient where Client = 1) C1 ON 1=1
LEFT JOIN (SELECT InitDate FROM InitClient where Client = 2) C2 ON 1=1
WHERE
(
(Stock.Client = 1 AND Stock.PostingDate > C1.InitDate) OR
(Stock.Client = 2 AND Stock.PostingDate > C2.InitDate)
)
样本数据集:
CREATE TABLE InitClient
(
Client varchar(300),
InitDate date
);
INSERT INTO InitClient (Client,InitDate)
VALUES
('1', '5/1/2021'),
('2', '1/31/2021');
SELECT * FROM InitClient
CREATE TABLE Stock
(
Material varchar(300),
PostingDate varchar(300),
Stock varchar(300),
Client varchar(300)
);
INSERT INTO Stock (Material,PostingDate,Stock,Client)
VALUES
('322', '1/1/2021', '5', '1'),
('101', '2/1/2021', '5', '2'),
('322', '3/2/2021', '10', '1'),
('101', '4/13/2021', '5', '1'),
('400', '5/11/2021', '170', '2'),
('401', '6/20/2021', '200', '1'),
('322', '7/20/2021', '160', '2'),
('400', '8/9/2021', '93', '2');
SELECT * FROM Stock
期望的结果,但随后使用 OR 语句的替代品来提高性能:
| Material | PostingDate | Stock | Client |
|----------|-------------|-------|--------|
| 322 | 1/1/2021 | 5 | 1 |
| 101 | 2/1/2021 | 5 | 2 |
| 322 | 3/2/2021 | 10 | 1 |
| 101 | 4/13/2021 | 5 | 1 |
| 400 | 5/11/2021 | 170 | 2 |
| 401 | 6/20/2021 | 200 | 1 |
| 322 | 7/20/2021 | 160 | 2 |
| 400 | 8/9/2021 | 93 | 2 |
如果在上面的代码中可以替代以保持性能,同时使其动态化,有什么建议吗?
解决方案
您可以对这个查询进行相当多的优化。
- 首先,这两个
LEFT JOIN
s 基本上只是半连接,因为您实际上并没有从它们返回任何结果。所以我们可以把它们变成一个单一的EXISTS
. - 您还将获得到 的隐式转换
int
,因为Client
isvarchar
和1,2
is anint
。因此,将其更改为'1','2'
,或者您可以更改列类型。 PostingDate
也是varchar
,那应该是真的date
SELECT
s.Material
,s.Stock
,s.PostingDate
,s.Client
FROM Stock s
WHERE s.Client IN ('1','2')
AND EXISTS (SELECT 1
FROM InitClient c
WHERE s.PostingDate > c.InitDate
AND c.Client = s.Client
);
- 接下来,您要查看索引。对于此查询(不考虑正在运行的任何其他查询),您可能需要以下索引(删除
INCLUDE
聚集索引)
InitClient (Client, InitDate)
Stock (Client) INCLUDE (PostingDate, Material, Stock)
- 即使使用这些索引,您也可能会在 上进行扫描
Stock
,因为IN
功能类似于OR
. 这并不总是发生,值得检查。如果是这样,您可以改写它以使用UNION ALL
SELECT
s.Material
,s.Stock
,s.PostingDate
,s.Client
FROM (
SELECT *
FROM Stock s
WHERE s.Client = '1'
UNION ALL
SELECT *
FROM Stock s
WHERE s.Client = '2'
) s
WHERE EXISTS (SELECT 1
FROM InitClient c
WHERE s.PostingDate > c.InitDate
AND c.Client = s.Client
);
推荐阅读
- forms - Symfony 4.3 动态形式的标签解决方案与 Doctrine 多对一关联
- android - Parcelable Kotlin 的 Parcelable 对象列表
- reactjs - 将多个组件连接到 React Redux Store
- processing - 如何下载网络库进行处理?
- php - 通过 Laravel/Eloquent 插入 MySQL 行的问题
- android - 如何在主题更改时制作循环显示效果?(就像在 Telegram 或 VK 中一样)
- angular - 如何在不更改 URL 路径的情况下导航到 Angular 中的新页面,但在位置历史记录中推送新状态?
- html - 有没有更简洁的方式来声明 div、类、id 和其他 html 元素?
- ios - 如何在iOS中除状态栏区域外全屏显示模式?
- android - 给定房间查询中的特定参数,如何从房间数据库中选择特定列?