为啥 SQL 查询在最合适的时候不使用主键进行 SELECT?

Posted

技术标签:

【中文标题】为啥 SQL 查询在最合适的时候不使用主键进行 SELECT?【英文标题】:Why does SQL query not use primary key for SELECT when it is most suitable?为什么 SQL 查询在最合适的时候不使用主键进行 SELECT? 【发布时间】:2016-03-08 06:30:03 【问题描述】:

场景(试图与我的生产场景进行 1-1 映射):获取从纽约乘坐维珍航空公司或阿联酋航空的所有人员的列表。

表格tbl_Flyer 有几列包含有关在任何时间点飞行的人员的所有详细信息。主键是CountryId, CityId, AirlineId, PersonId

现在,一个简单的 SQL 查询如下所示:

SELECT  flyer.PersonId
FROM    tbl_Flyer passenger
WHERE   passenger.CountryId = @countryId
        AND passenger.City= @cityId
        AND passenger.AirlineId IN (SELECT values FROM @allAirlineIds)

@countryId@cityId@allAirlineIds 被正确发送到 SQL 存储过程。我的假设是该查询将使用主键,因为查询中使用的所有 4 列都存在于 PK 中,但由于某种原因它没有。它使用添加的非聚集索引能够根据年龄、性别等个人详细信息查询乘客。 (看起来像(CountryId、CityId、Age、Sex))

我正在向查询中添加 ForceSeek 提示,但我想了解是否存在我可能在此处使用的反模式?知道为什么 SQL 会违反逻辑而不使用 PK 进行搜索吗?

【问题讨论】:

你应该避免构建如此复杂的主键。将代理标识列作为主键,并为您想要的任何列组合附加唯一约束。我建议始终使用exists 而不是in (select... 主键帮助我对数据库中的数据进行分区,我没有其他直接的选项来根据国家/地区对数据进行分区。 不确定按国家/地区划分是否对航班数据有意义,但您可能有更好的理解。这是一个真正的 partitioning 还是只是一个“影响物理行顺序”的聚集索引? Country 反正选择性不好。没有那么多国家。您的索引从国家/地区开始,在@allairlineids 上您有一些估计问题。 只是一个猜测,但因为它是分区的,所以效率不高 好的,假设应用程序不允许您输入国家/城市数据。它会自动检测您的位置并输入这些字段......抱歉,我不得不在这里玩弄国家类比。 【参考方案1】:

您的数据库引擎使用一个或另一个索引的选择是基于自动启发式自动进行的……这并不总是最准确的。 (99% 的时候,他们是,但有时,人脑找到了更好的方法)。 这种启发式是根据通用规则计算的,有时它与数据库内容的实际情况不匹配。(字符串冒号总是相同的第一个字母,冒号有很多空值,...)

必须对表的每一行进行“Select In”操作,并且库存充足,并且被大多数数据库引擎认为是极其昂贵的,因此您的数据库可以优先使用另一种方式。(非聚集索引在你的情况下)

顺便说一句,使用 Exist in 被认为成本要低得多,并且会使您的数据库引擎更容易选择索引。

如果还不够,请使用 ForceSeek。

如果 CountryId、CityId、AirlineId、PersonId 的类型与 @CountryId、@CityId、@AirlineId、@PersonId 的类型不同(类型转换很昂贵),您也会遇到同样的问题

【讨论】:

以上是关于为啥 SQL 查询在最合适的时候不使用主键进行 SELECT?的主要内容,如果未能解决你的问题,请参考以下文章

mysql 联表查询后,将某个字段的特定值排序在最前面

mySql为啥查询时有时快,有时慢

为啥 SQL Server 查询优化器有时会忽略明显的聚集主键?

在Oracle中定义SQL查询。索引为啥不能直接从select语句中引用?求教,谢谢

MS SQL 2008 设置主键 该列值为啥还能重复

此 SQL 查询的解决方案?