Mengapa SELECT COUNT () rencana pelaksanaan kueri menyertakan tabel bergabung kiri?

9

Dalam SQL Server 2012 saya memiliki fungsi bernilai tabel dengan bergabung ke tabel lain saya perlu menghitung jumlah baris untuk 'fungsi bernilai tabel' ini. Ketika saya memeriksa rencana eksekusi, saya bisa melihat tabel gabung kiri. Mengapa? Bagaimana tabel kiri bergabung memengaruhi jumlah baris yang dikembalikan? Saya berharap bahwa mesin db tidak perlu mengevaluasi tabel bersama kiri dalam hitungan SELECT (..).

Select count(realtyId) FROM [dbo].[GetFilteredRealtyFulltext]('"praha"')

Rencana pelaksanaan:

masukkan deskripsi gambar di sini

Fungsi tabel dihargai:

CREATE FUNCTION [dbo].[GetFilteredRealtyFulltext]
(@criteria nvarchar(4000))
RETURNS TABLE
AS
RETURN (SELECT 
realty.Id AS realtyId,
realty.OwnerId,
realty.Caption AS realtyCaption,
realty.BusinessCategory,
realty.Created,
realty.LastChanged,
realty.LastChangedType,
realty.Price,
realty.Pricing,
realty.PriceCurrency,
realty.PriceNote,
realty.PricePlus,
realty.OfferState,
realty.OrderCode,
realty.PublishAddress,
realty.PublishMap,
realty.AreaLand,
realty.AreaCover,
realty.AreaFloor,
realty.Views,
realty.TopPoints,
realty.Radius,
COALESCE(realty.Wgs84X, ruian_cobce.Wgs84X, ruian_obec.Wgs84X) as Wgs84X,
COALESCE(realty.Wgs84Y, ruian_cobce.Wgs84Y, ruian_obec.Wgs84Y) as Wgs84Y,
realty.krajId,
realty.okresId,
realty.obecId,
realty.cobceId,
IsNull(CONVERT(int,realty.Ranking),0) as Ranking,

realty.energy_efficiency_rating,
realty.energy_performance_attachment,
realty.energy_performance_certificate,
realty.energy_performance_summary,

Category.Id AS CategoryId,
Category.ParentCategoryId,
Category.WholeName,
okres.nazev AS okres,
ruian_obec.nazev AS obec,
ruian_cobce.nazev AS cobce,
ExternFile.ServerPath,
Person.ParentPersonId,
( COALESCE(ftR.Rank,0) + COALESCE(ftObec.Rank,0) + COALESCE(ftOkres.Rank,0) + COALESCE(ftpobvod.Rank,0)) AS FtRank

FROM realty
JOIN Category ON realty.CategoryId = Category.Id
LEFT JOIN ruian_cobce ON realty.cobceId = ruian_cobce.cobce_kod
LEFT JOIN ruian_obec ON realty.obecId = ruian_obec.obec_kod
LEFT JOIN okres ON realty.okresId = okres.okres_kod
LEFT JOIN ExternFile ON realty.Id = ExternFile.ForeignId AND ExternFile.IsMain = 1 AND ExternFile.ForeignTable = 5
INNER JOIN Person ON realty.OwnerId = Person.Id
Left JOIN CONTAINSTABLE(Realty, *, @criteria) ftR ON realty.Id = ftR.[Key] 
Left JOIN CONTAINSTABLE(ruian_obec, *, @criteria) ftObec ON realty.obecId = ftObec.[Key] 
Left JOIN CONTAINSTABLE(Okres, *, @criteria) ftOkres ON realty.okresId = ftOkres.[Key]
Left JOIN CONTAINSTABLE(pobvod, *, @criteria) ftpobvod ON realty.pobvodId = ftpobvod.[Key]
WHERE Person.ConfirmStatus = 1
AND ( COALESCE(ftR.Rank,0) + COALESCE(ftObec.Rank,0) + COALESCE(ftOkres.Rank,0) + COALESCE(ftpobvod.Rank,0))  > 0
)

MEMPERBARUI:

Saya menambahkan indeks unik untuk mengikuti gagasan Rob Farley:

 Create unique nonclustered index ExternFileIsMainUnique ON ExternFile(ForeignId) WHERE IsMain = 1 AND ForeignTable = 5

Dan diindeks disarankan oleh DB Engine:

CREATE NONCLUSTERED INDEX [RealtyOwnerLocation] ON [dbo].[Realty]

([OwnerId] ASC) TERMASUK ([Id], [okresId], [obecId], [pobvodId]) GO

Untuk kesederhanaan, saya menghapus kondisi

WHERE Person.ConfirmStatus = 1

dari fungsi yang dinilai tabel di atas.

Sekarang rencana pelaksanaannya jauh lebih sederhana tetapi masih menyentuh tabel ExternFile:

masukkan deskripsi gambar di sini

Mungkin sql server tidak cukup pintar?

Tomas Kubes
sumber

Jawaban:

12

Jika ForeignId, ForeignTable, IsMaintidak diketahui * sebagai unik ExternFile, maka QO perlu menyertakan tabel itu untuk menghitung. Setiap kali beberapa baris cocok, jumlah akan terpengaruh.

Bergabunglah Penyederhanaan dalam
Perancangan SQL Server untuk penyederhanaan (perekaman SQLBits)


* Pengoptimal saat ini tidak mengenali indeks unik yang difilter sebagai unik

UPDATE (oleh OP) : Solusinya adalah mengubah baris dalam kueri dari LEFT JOIN (yang dapat menghasilkan beberapa baris):

LEFT JOIN ExternFile ON realty.Id = ExternFile.ForeignId AND ExternFile.IsMain = 1 AND ExternFile.ForeignTable = 5

untuk OUTER BERLAKU dengan TOP (yang menghasilkan satu baris dan tidak mempengaruhi COUNT)

OUTER APPLY (SELECT TOP (1) ServerPath FROM ExternFile WHERE ForeignId = realty.Id AND IsMain = 1 AND ForeignTable = 5) AS ExternFile

Permintaan sekarang lebih efektif. Menambahkan indeks unik tidak dapat dilakukan, karena nilai tidak unik, mereka unik hanya untuk kombinasi dalam kondisi dan ini tidak dianggap sebagai unik seperti yang disebutkan di atas.

Rob Farley
sumber