如何在不破坏SQL逻辑的情况下将JOINS转换为子查询

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在不破坏SQL逻辑的情况下将JOINS转换为子查询相关的知识,希望对你有一定的参考价值。

我在T-SQL中使用多个连接时遇到了性能问题,如果有人可以帮助我将这些连接转换为子查询,那就太好了。

每当我尝试将连接更改为子查询时,我都会丢失特定表的名称声明。例如,如果我尝试将Album join(这是下面代码的第一个连接)转换为子查询,我将失去别名“AS a”并且“a.Title AS Album”停止工作,所以我不知道这是怎么回事会做的。如果有人给我一个例子,它应该如何适用于其中一个案例,我想我将能够重建所有这些案例。

SQL

SELECT
    t.TrackId, 
    t.[Name] AS Track, 
    a.Title AS Album, 
    aa.[Name] AS Artist, 
    p.[Name] AS Playlist,
    m.[Name] AS MediaType,
    il.UnitPrice AS InvoicePrice,
    CONCAT(c.FirstName, ' ', c.LastName) AS CustomerName,
    CONCAT(e.FirstName, ' ', e.LastName) AS ResponsibleEmployeeName
FROM dbo.Track AS t
INNER JOIN dbo.Album AS a
    ON t.AlbumId = a.AlbumId
INNER JOIN dbo.Artist AS aa
    ON a.ArtistId = aa.ArtistId
INNER JOIN dbo.PlaylistTrack AS plt
    ON t.TrackId = plt.TrackId
INNER JOIN dbo.Playlist AS p
    ON p.PlaylistId = plt.PlaylistId
INNER JOIN dbo.MediaType AS m
    ON t.MediaTypeId = m.MediaTypeId
INNER JOIN dbo.InvoiceLine AS il
    ON t.TrackId = il.TrackId
INNER JOIN dbo.Invoice AS i
    ON il.InvoiceId = i.InvoiceId
INNER JOIN dbo.Customer AS c
    ON i.CustomerId = c.CustomerId
INNER JOIN dbo.Employee AS e
    ON c.SupportRepId = e.EmployeeId
WHERE m.[Name] LIKE '%audio%'
ORDER BY t.[Name] ASC
答案

你的意思是什么样的子查询?就像是:

SELECT t.TrackId, 
    t.[Name] AS Track, 
    (SELECT title FROM dbo.Album WHERE AlbumId = t.AlbumId) AS AlbumTitle

如果你删除Album的连接,那就不行了,因为你需要专辑参考才能找到艺术家。如果要加入子查询,可以执行此操作,并保留别名:

SELECT
    t.TrackId, 
    t.[Name] AS Track, 
    a.Title AS Album, 
    aa.[Name] AS Artist, 
    p.[Name] AS Playlist,
    m.[Name] AS MediaType,
    il.UnitPrice AS InvoicePrice,
    CONCAT(c.FirstName, ' ', c.LastName) AS CustomerName,
    CONCAT(e.FirstName, ' ', e.LastName) AS ResponsibleEmployeeName
FROM dbo.Track AS t
INNER JOIN (SELECT * FROM dbo.Album) AS a
   ON t.AlbumId = a.AlbumId
   -- rest of joins

但从逻辑上讲,这与您现在完全相同,并且查询优化器生成的计划没有区别。即使这样:

SELECT
    t.TrackId, 
    t.[Name] AS Track, 
    aa.Title AS Album, -- note change here
    aa.[Name] AS Artist, 
    p.[Name] AS Playlist,
    m.[Name] AS MediaType,
    il.UnitPrice AS InvoicePrice,
    CONCAT(c.FirstName, ' ', c.LastName) AS CustomerName,
    CONCAT(e.FirstName, ' ', e.LastName) AS ResponsibleEmployeeName
FROM dbo.Track AS t
INNER JOIN (SELECT alb.Title
                 , art.Name
                 , alb.AlbumId
              FROM dbo.Album alb
        INNER JOIN dbo.Artist art
                ON art.ArtistId = alb.ArtistID) AS aa
   ON t.AlbumId = aa.AlbumId
INNER JOIN dbo.PlaylistTrack AS plt
   ON t.TrackId = plt.TrackId
-- rest of them

将产生完全相同的计划。我们可能已经将Album Artist连接移动到一个连接的子查询,但实质上,它仍然是相同的东西 - 内部连接。

没有这样的事情会对你的表现有所帮助。可能对您有帮助的是在这些表上创建索引。如果这是您执行的查询类型,您还可以创建索引视图,如:

CREATE VIEW BoughtTracks
WITH SCHEMABINDING
AS
SELECT
    il.InvoiceLineId, -- I'm guessing here, we need a unique ID
    t.TrackId, 
    t.[Name] AS Track, 
    a.Title AS Album, 
    aa.[Name] AS Artist, 
    p.[Name] AS Playlist,
    m.[Name] AS MediaType,
    il.UnitPrice AS InvoicePrice,
    CONCAT(c.FirstName, ' ', c.LastName) AS CustomerName,
    CONCAT(e.FirstName, ' ', e.LastName) AS ResponsibleEmployeeName
FROM dbo.Track AS t
INNER JOIN dbo.Album AS a
    ON t.AlbumId = a.AlbumId
INNER JOIN dbo.Artist AS aa
    ON a.ArtistId = aa.ArtistId
INNER JOIN dbo.PlaylistTrack AS plt
    ON t.TrackId = plt.TrackId
INNER JOIN dbo.Playlist AS p
    ON p.PlaylistId = plt.PlaylistId
INNER JOIN dbo.MediaType AS m
    ON t.MediaTypeId = m.MediaTypeId
INNER JOIN dbo.InvoiceLine AS il
    ON t.TrackId = il.TrackId
INNER JOIN dbo.Invoice AS i
    ON il.InvoiceId = i.InvoiceId
INNER JOIN dbo.Customer AS c
    ON i.CustomerId = c.CustomerId
INNER JOIN dbo.Employee AS e
    ON c.SupportRepId = e.EmployeeId
WHERE m.[Name] LIKE '%audio%'

CREATE UNIQUE CLUSTERED INDEX ux ON BoughtTracks (InvoiceLineId);

这将减慢对这些表的插入,但在BoughtTracks上选择将很快(您还可以在该视图上创建其他索引),例如:

SELECT * 
  FROM BoughtTracks WITH (NOEXPAND) -- NOEXPAND is important
 WHERE CustomerName = 'Joe Smith'

可以执行比当前查询更快的数量级,具体取决于数据的大小。特别是如果你在它上面创建一个索引

CREATE INDEX ix_CustomerName ON BoughtTracks (CustomerName) 
INCLUDE (...) -- maybe include some columns you know you will need when querying for CustomerName
WHERE (...) -- maybe there are alsways accompanying predicates when querying for CustomerName
另一答案

在子查询中转换连接可能不是最佳解决方案

假设您已经拥有每个零售表的外键索引

table  Artist index on column (ArtistId)
table  PlaylistTrack index on column (TrackId)
table  Playlist index on column (PlaylistId)
table  MediaType index  on column ( MediaTypeId )
.....

为了性能,请确保您有索引

table  track a composite index  on column  (AlbumId, TrackId, MediaTypeId )
table  Album  a cmposite index  on column ( AlbumId, ArtistId )

以上是关于如何在不破坏SQL逻辑的情况下将JOINS转换为子查询的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用数据透视的情况下将行转换或转置为 SQL 中的列?

如何在不破坏滚动功能的情况下将 UIImage 添加到 UIScrollView?

如何在不破坏单词的情况下将字符串拆分为行?

如何在不破坏 UI 的情况下将搜索放在另一个线程中?

Jquery 循环 - 如何在不破坏缩略图寻呼机的情况下将图像包装在跨度标签中?

如何在不破坏我的结构的情况下将特定单元格排除到 BigQuery 中的数组数组中?