web-dev-qa-db-ja.com

実行計画からハッシュ一致を削除するために必要なインデックスを決定する方法

Azure上のSQL Serverデータベース(Sql Server 2014/12.0.2000.8)

私は、すべて同じ基本的な選択プロパティと結合を持つストアドプロシージャを多数持っているため、すべてを網羅するビューを生成することにしました。次に、個々のストアドプロシージャで、追加のフィルタリングを行います。

ビューの実行プランに2つの異なるハッシュ一致(内部結合)部分が表示され、クエリが遅くなっていることに今日気づきました。実行計画から、最適化、または追加できる可能性のあるインデックスを特定するために、どの列または結合を確認する必要があるかを判断できません。

リンクされているのは、問題の実行プランです。 https://drive.google.com/file/d/1VeVrfD_usowEiPEn0KuVmREHl1CWCtxx/view?usp=sharing

Execプランを見ると、「ハッシュキープローブ」と「プローブ残差」セクションが記載されているため、プローブセクションの2つの列を使用して別のインデックスを追加しようとしましたが、うまくいきませんでした。これらのテーブルには既に大量のインデックスが作成されているため、困っています。

実行計画に沿ったクエリは次のとおりです。

select  s.Division,
        wr.Season,
        wr.WeekNumber,
        wr.RankingDate,
        wr.IsCurrent,
        wr.WeightClass,
        wr.[Rank],
        wr.WrestlerId,
        w.LastName,
        w.FirstName,
        s.SchoolId,
        s.[Name] [SchoolName],
        coalesce(tr.DualRank, tr.TournamentRank, tr.[Rank]) [SchoolRank],
        s.Conference,
        s.ConferenceSeo,
        dbo.uf_GetEligibilityString(r.WrestlerId, r.Season) [EligibilityYear],
        w.HasRedshirted,
        r.Starter,
        r.IsRedshirting,
        r.IsInjured,
        dbo.uf_GetRecordString(st.Wins, st.Losses) [Record],
        dbo.uf_GetRecordString(st.ConfWins, st.ConfLosses) [ConferenceRecord],
        dbo.uf_GetPercentageString(st.Wins, (st.Wins + st.Losses)) [WinPercentage],
        dbo.uf_GetPercentageString(st.MajorsFor + st.TechFall4For + st.TechFall5For + st.FallsFor, st.Wins) [BonusPercentage],
        st.Wins,
        st.Losses,
        wr.EloRank [EloPoints],
        0 [Trend],
        999 [PreviousWeek]
from    dbo.WrestlerRanking wr
join    dbo.Wrestler w on w.WrestlerId = wr.WrestlerId
join    dbo.Roster r on r.Season = wr.Season
    and r.WrestlerId = wr.WrestlerId
join    dbo.[Stats] st on st.Season = wr.Season
    and st.WrestlerId = wr.WrestlerId
join    dbo.School s on s.SchoolId = r.SchoolId
join    dbo.TeamRankings tr on tr.Season = wr.Season
    and tr.WeekNumber = wr.WeekNumber
    and tr.SchoolId = s.SchoolId
where   wr.[Rank] is not null

-- above here is the view, below here are the additional filters/where clauses added by the calling stored procedure

and     s.Division = 'Division I'
and     wr.Season = 2019
and     r.Starter = 1

テーブルの定義は次のとおりです。

レスラーランキング

CREATE TABLE [dbo].[WrestlerRanking](
    [WrestlerRankingId] [int] IDENTITY(1,1) NOT NULL,
    [WrestlerId] [int] NOT NULL,
    [Season] [int] NOT NULL,
    [WeekNumber] [int] NOT NULL,
    [Rank] [int] NULL,
    [EloRank] [decimal](7, 2) NOT NULL,
    [RankingDate] [datetime] NOT NULL,
    [IsCurrent] [bit] NOT NULL,
    [WeightClass] [int] NOT NULL,
    [TournamentPoints] [decimal](3, 1) NOT NULL,
    [TournamentBonus] [decimal](4, 2) NOT NULL,
    [Division] [varchar](12) NOT NULL,
    [StarterRank] [int] NULL,
 CONSTRAINT [pk_WrestlerRanking_Season_WrestlerId_WeekNumber] PRIMARY KEY CLUSTERED 
(
    [Season] ASC,
    [WrestlerId] ASC,
    [WeekNumber] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)
)
GO

ALTER TABLE [dbo].[WrestlerRanking] ADD  DEFAULT ((0.0)) FOR [TournamentPoints]
GO

ALTER TABLE [dbo].[WrestlerRanking] ADD  DEFAULT ((0.00)) FOR [TournamentBonus]
GO

ALTER TABLE [dbo].[WrestlerRanking] ADD  DEFAULT ('Division I') FOR [Division]
GO

ALTER TABLE [dbo].[WrestlerRanking]  WITH NOCHECK ADD  CONSTRAINT [fk_WrestlerRanking_Wrestler] FOREIGN KEY([WrestlerId])
REFERENCES [dbo].[Wrestler] ([WrestlerId])
GO

ALTER TABLE [dbo].[WrestlerRanking] CHECK CONSTRAINT [fk_WrestlerRanking_Wrestler]
GO

レスラー:

CREATE TABLE [dbo].[Wrestler](
    [WrestlerId] [int] IDENTITY(1,1) NOT NULL,
    [LastName] [varchar](100) NOT NULL,
    [FirstName] [varchar](100) NOT NULL,
    [Aka] [varchar](100) NULL,
    [UpdatedBy] [varchar](50) NULL,
    [UpdatedDate] [datetime] NULL,
    [HasRedshirted] [bit] NOT NULL,
    [TwitterHandle] [varchar](56) NULL,
    [CreatedBy] [varchar](50) NOT NULL,
    [CreatedDate] [datetime] NULL,
    [IsForfeit] [bit] NOT NULL,
 CONSTRAINT [PK_Wrestler] PRIMARY KEY CLUSTERED 
(
    [WrestlerId] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)
)
GO

ALTER TABLE [dbo].[Wrestler] ADD  DEFAULT ((1)) FOR [HasRedshirted]
GO

ALTER TABLE [dbo].[Wrestler] ADD  DEFAULT ('service') FOR [CreatedBy]
GO

ALTER TABLE [dbo].[Wrestler] ADD  DEFAULT (getutcdate()) FOR [CreatedDate]
GO

ALTER TABLE [dbo].[Wrestler] ADD  DEFAULT ((0)) FOR [IsForfeit]
GO

名簿:

CREATE TABLE [dbo].[Roster](
    [RosterId] [int] IDENTITY(1,1) NOT NULL,
    [Season] [int] NOT NULL,
    [SchoolId] [int] NOT NULL,
    [WrestlerId] [int] NOT NULL,
    [IsRedshirting] [bit] NOT NULL,
    [EligibilityYear] [varchar](2) NOT NULL,
    [WeightClass] [int] NOT NULL,
    [Starter] [bit] NOT NULL,
    [CreatedBy] [varchar](50) NOT NULL,
    [CreatedDate] [datetime] NOT NULL,
    [UpdatedBy] [varchar](50) NULL,
    [UpdatedDate] [datetime] NULL,
    [IsInjured] [bit] NOT NULL,
    [NationalParticipant] [bit] NOT NULL,
PRIMARY KEY NONCLUSTERED 
(
    [RosterId] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF),
 CONSTRAINT [unique_Wrestler_Season] UNIQUE NONCLUSTERED 
(
    [Season] ASC,
    [WrestlerId] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)
)
GO

ALTER TABLE [dbo].[Roster] ADD  DEFAULT ((0)) FOR [IsRedshirting]
GO

ALTER TABLE [dbo].[Roster] ADD  DEFAULT ('UN') FOR [EligibilityYear]
GO

ALTER TABLE [dbo].[Roster] ADD  DEFAULT ((0)) FOR [WeightClass]
GO

ALTER TABLE [dbo].[Roster] ADD  DEFAULT ((0)) FOR [Starter]
GO

ALTER TABLE [dbo].[Roster] ADD  DEFAULT ('service') FOR [CreatedBy]
GO

ALTER TABLE [dbo].[Roster] ADD  DEFAULT (getutcdate()) FOR [CreatedDate]
GO

ALTER TABLE [dbo].[Roster] ADD  DEFAULT ((0)) FOR [IsInjured]
GO

ALTER TABLE [dbo].[Roster] ADD  DEFAULT ((0)) FOR [NationalParticipant]
GO

ALTER TABLE [dbo].[Roster]  WITH NOCHECK ADD  CONSTRAINT [fk_Roster_School] FOREIGN KEY([SchoolId])
REFERENCES [dbo].[School] ([SchoolId])
GO

ALTER TABLE [dbo].[Roster] CHECK CONSTRAINT [fk_Roster_School]
GO

ALTER TABLE [dbo].[Roster]  WITH NOCHECK ADD  CONSTRAINT [fk_Roster_Wrestler] FOREIGN KEY([WrestlerId])
REFERENCES [dbo].[Wrestler] ([WrestlerId])
GO

ALTER TABLE [dbo].[Roster] CHECK CONSTRAINT [fk_Roster_Wrestler]
GO

統計:

CREATE TABLE [dbo].[Stats](
    [StatsId] [int] IDENTITY(1,1) NOT NULL,
    [WrestlerId] [int] NOT NULL,
    [Wins] [int] NOT NULL,
    [Losses] [int] NOT NULL,
    [MajorsFor] [int] NOT NULL,
    [MajorsAgainst] [int] NOT NULL,
    [TechFall4For] [int] NOT NULL,
    [TechFall4Against] [int] NOT NULL,
    [TechFall5For] [int] NOT NULL,
    [TechFall5Against] [int] NOT NULL,
    [FallsFor] [int] NOT NULL,
    [FallsAgainst] [int] NOT NULL,
    [OtherWins] [int] NOT NULL,
    [OtherLosses] [int] NOT NULL,
    [AvgPointsFor] [decimal](4, 2) NULL,
    [AvgPointsAgainst] [decimal](4, 2) NULL,
    [BonusPercentage] [decimal](7, 4) NULL,
    [WinsRank] [int] NULL,
    [MDsRank] [int] NULL,
    [T4sRank] [int] NULL,
    [T5sRank] [int] NULL,
    [FallsRank] [int] NULL,
    [BonusPctRank] [int] NULL,
    [AvgPointsForRank] [int] NULL,
    [AvgPointsAgainstRank] [int] NULL,
    [ConfWins] [int] NULL,
    [ConfLosses] [int] NULL,
    [ConfMajorsFor] [int] NULL,
    [ConfMajorsAgainst] [int] NULL,
    [ConfT4For] [int] NULL,
    [ConfT4Against] [int] NULL,
    [ConfT5For] [int] NULL,
    [ConfT5Against] [int] NULL,
    [ConfFallsFor] [int] NULL,
    [ConfFallsAgainst] [int] NULL,
    [ConfOtherWins] [int] NULL,
    [ConfOtherLosses] [int] NULL,
    [ConfAvgPointsFor] [decimal](4, 2) NULL,
    [ConfAvgPointsAgainst] [decimal](4, 2) NULL,
    [ConfBonusPercentage] [decimal](7, 4) NULL,
    [ConfWinsRank] [int] NULL,
    [ConfMDsRank] [int] NULL,
    [ConfT4sRank] [int] NULL,
    [ConfT5sRank] [int] NULL,
    [ConfFallsRank] [int] NULL,
    [ConfBonusPctRank] [int] NULL,
    [ConfAvgPointsForRank] [int] NULL,
    [ConfAvgPointsAgainstRank] [int] NULL,
    [Season] [int] NOT NULL,
PRIMARY KEY NONCLUSTERED 
(
    [StatsId] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF),
 CONSTRAINT [unique_Season_Wrestler] UNIQUE NONCLUSTERED 
(
    [Season] ASC,
    [WrestlerId] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)
)
GO

ALTER TABLE [dbo].[Stats] ADD  DEFAULT ((0)) FOR [Wins]
GO

ALTER TABLE [dbo].[Stats] ADD  DEFAULT ((0)) FOR [Losses]
GO

ALTER TABLE [dbo].[Stats] ADD  DEFAULT ((0)) FOR [MajorsFor]
GO

ALTER TABLE [dbo].[Stats] ADD  DEFAULT ((0)) FOR [MajorsAgainst]
GO

ALTER TABLE [dbo].[Stats] ADD  DEFAULT ((0)) FOR [TechFall4For]
GO

ALTER TABLE [dbo].[Stats] ADD  DEFAULT ((0)) FOR [TechFall4Against]
GO

ALTER TABLE [dbo].[Stats] ADD  DEFAULT ((0)) FOR [TechFall5For]
GO

ALTER TABLE [dbo].[Stats] ADD  DEFAULT ((0)) FOR [TechFall5Against]
GO

ALTER TABLE [dbo].[Stats] ADD  DEFAULT ((0)) FOR [FallsFor]
GO

ALTER TABLE [dbo].[Stats] ADD  DEFAULT ((0)) FOR [FallsAgainst]
GO

ALTER TABLE [dbo].[Stats] ADD  DEFAULT ((0)) FOR [OtherWins]
GO

ALTER TABLE [dbo].[Stats] ADD  DEFAULT ((0)) FOR [OtherLosses]
GO

ALTER TABLE [dbo].[Stats] ADD  DEFAULT ((2015)) FOR [Season]
GO

ALTER TABLE [dbo].[Stats]  WITH NOCHECK ADD  CONSTRAINT [FK_Statistics_Wrestler] FOREIGN KEY([WrestlerId])
REFERENCES [dbo].[Wrestler] ([WrestlerId])
GO

ALTER TABLE [dbo].[Stats] CHECK CONSTRAINT [FK_Statistics_Wrestler]
GO

学校:

CREATE TABLE [dbo].[School](
    [SchoolId] [int] IDENTITY(1,1) NOT NULL,
    [Name] [varchar](100) NOT NULL,
    [Division] [varchar](100) NULL,
    [Conference] [varchar](100) NULL,
    [Nickname] [varchar](100) NULL,
    [Aka] [varchar](100) NULL,
    [IsActive] [bit] NOT NULL,
    [TwitterHandle] [varchar](56) NULL,
    [CreatedBy] [varchar](100) NOT NULL,
    [CreatedDate] [datetime] NOT NULL,
    [UpdatedBy] [varchar](100) NULL,
    [UpdatedDate] [datetime] NULL,
    [ConferenceSeo] [varchar](20) NULL,
    [ShopUrl] [varchar](2000) NULL,
PRIMARY KEY CLUSTERED 
(
    [SchoolId] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)
)
GO

ALTER TABLE [dbo].[School] ADD  DEFAULT ((1)) FOR [IsActive]
GO

ALTER TABLE [dbo].[School] ADD  DEFAULT (suser_sname()) FOR [CreatedBy]
GO

ALTER TABLE [dbo].[School] ADD  DEFAULT (getutcdate()) FOR [CreatedDate]
GO

TeamRankings:

CREATE TABLE [dbo].[TeamRankings](
    [TeamRankingsId] [int] IDENTITY(1,1) NOT NULL,
    [Points] [decimal](7, 2) NOT NULL,
    [Rank] [int] NULL,
    [SchoolId] [int] NOT NULL,
    [Season] [int] NOT NULL,
    [WeekNumber] [int] NOT NULL,
    [IsCurrent] [bit] NOT NULL,
    [RankingDate] [datetime] NOT NULL,
    [TournamentPoints] [decimal](5, 2) NOT NULL,
    [TournamentRank] [int] NOT NULL,
    [DualWins] [int] NULL,
    [DualLosses] [int] NULL,
    [DualRank] [int] NULL,
    [Division] [varchar](12) NOT NULL,
 CONSTRAINT [pk_TeamRankings_Season_SchoolId_WeekNumber] PRIMARY KEY CLUSTERED 
(
    [Season] ASC,
    [SchoolId] ASC,
    [WeekNumber] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)
)
GO

ALTER TABLE [dbo].[TeamRankings] ADD  DEFAULT ((2014)) FOR [Season]
GO

ALTER TABLE [dbo].[TeamRankings] ADD  DEFAULT ((0)) FOR [WeekNumber]
GO

ALTER TABLE [dbo].[TeamRankings] ADD  DEFAULT ((0)) FOR [IsCurrent]
GO

ALTER TABLE [dbo].[TeamRankings] ADD  DEFAULT ('1900-01-01') FOR [RankingDate]
GO

ALTER TABLE [dbo].[TeamRankings] ADD  DEFAULT ((0)) FOR [TournamentPoints]
GO

ALTER TABLE [dbo].[TeamRankings] ADD  DEFAULT ((999)) FOR [TournamentRank]
GO

ALTER TABLE [dbo].[TeamRankings] ADD  DEFAULT ((0)) FOR [DualWins]
GO

ALTER TABLE [dbo].[TeamRankings] ADD  DEFAULT ((0)) FOR [DualLosses]
GO

ALTER TABLE [dbo].[TeamRankings] ADD  DEFAULT ('Division I') FOR [Division]
GO

ALTER TABLE [dbo].[TeamRankings]  WITH NOCHECK ADD  CONSTRAINT [fk_TeamRankings_School] FOREIGN KEY([SchoolId])
REFERENCES [dbo].[School] ([SchoolId])
GO

ALTER TABLE [dbo].[TeamRankings] CHECK CONSTRAINT [fk_TeamRankings_School]
GO
3
ganders

実際のクエリプランでは、ほとんどの演算子を調べて、各演算子が使用するCPUと経過時間の概算を取得できます。これは、クエリプランの中で最も時間がかかった部分を特定するのに役立ちます。行モードのプランの場合、表示される数値はサブツリー全体の合計です。つまり、両方のハッシュ一致で使用されるCPUは13ミリ秒未満です。

enter image description here

クエリは合計192ミリ秒のCPUを使用します。クエリのパフォーマンスを調整する必要がある場合は、クエリ内のユーザー定義関数に焦点を当てます。実際の計画では、実行時間の約80%がこれらの関数に費やされていることが示されています。それらを最適化することは可能かもしれません。スカラUDFはパフォーマンスが大幅に低下する可能性があるため、最善の解決策はそれらを完全に削除することです。

元の質問に戻り、別のプランでパフォーマンスがどのように見えるかを確認したい場合は、次のキー列でインデックスを作成することをお勧めします:Season, WrestlerId, WeekNumber。インデックスがカバーするように、含まれる列も追加する必要があります。理想的には、1つのインデックスに、クエリに必要なすべてのキーとインデックス付きの列が必要です。一部のキー列を含む別のインデックスを追加することは、このクエリに最適なアプローチではない場合があります。

idx_NC_WrestlerRanking_Season_WeekNumber_Divisionインデックスはすでにカバーしているので、単にそのインデックスを変更することが可能かもしれません。インデックスの定義は質問に含まれていないため、詳細を述べるのは困難です。

2
Joe Obbish