4

世界の各町の気温を測るアプリがあります。各測定値は 5 分ごとに取得され、測定テーブルに書き込まれます。

CREATE TABLE [dbo].[Measurement](
    [MeasurementID] [int] IDENTITY(1,1) NOT NULL,
    [Town] [varchar](50) NOT NULL,
    [Date] [datetime] NOT NULL,
    [Temp] [int] NOT NULL,
CONSTRAINT [PK_Measurement] PRIMARY KEY CLUSTERED 
(
    [MeasurementID] ASC
)) ON [PRIMARY]

質問

町とその現在の気温のリストを取得するための最も効率的なクエリは何ですか?

10 万の町と 1000 万のレコードがあると仮定します。

注: いくつかの可能な回答を追加しましたが、おそらく他のオプションがあります。

4

6 に答える 6

4

動作するはずのカップルを次に示します。

SELECT
m1.Town, m1.Temp
FROM
Measurement AS m1
LEFT JOIN
Measurement AS m2
ON
m1.Town = m2.Town
AND m1.Date < m2.Date
WHERE
m2.MeasurementID IS NULL
ORDER BY m1.Town


町と日付のインデックスが必要です。

この手法は、より明白な問題を処理できなかった初期バージョンの MySQL で特に役立ちます。

SELECT Town, Temp
FROM Measurement AS m1
WHERE NOT EXISTS (
SELECT 1 FROM Measurement
WHERE Town = m1.Town
AND Date > m1.date
)
ORDER BY Town

于 2008-11-17T19:16:46.540 に答える
1
select *
from
(
    select distinct *, --Keyword,Total,CreatedOn,EngineInstanceID,
    Rank() over (PARTITION by Town order by Date DESC) as DateOrder
    from Measurement
    where Town is not null
) CurrentMeasurement
where DateOrder = 1
于 2008-11-17T19:09:35.540 に答える
1

この猫の皮を剥く方法がたくさんあるのを見るのは良いことです。これは CTE を使用したものです (より多くの ANSI 主義のためにクエリをネストすることもできますが、CTE は、前もって多くのインデントや宣言を避けるのに最適であることがわかりました)。

WITH LastMeasurements AS (
    SELECT [Town], MAX([Date]) AS LastMeasurementDate
    FROM [Measurement]
    GROUP BY [Town]
)
SELECT [Measurement].Town, [Measurement].[Date], [Measurement].Temp
FROM [Measurement]
INNER JOIN LastMeasurements
    ON [Measurement].[Town] = LastMeasurements.[Town]
    AND [Measurement].[Date] = LastMeasurements.LastMeasurementDate

明示的なシーク バック テクニックについて私が気に入っているのは、グループ用に選択された一番上の行にあるすべての情報に簡単にアクセスでき、グループ化の変更が非常に柔軟で、同じことを繰り返す必要が少ないことです。

オプティマイザーは、SQL Server でこれらを非常に迅速に実行する傾向があります。ほとんどのソリューションと同様に、Town、Date、Temp にインデックスがある場合、これはカバーされ、非常に高速に実行されます。街、デートだけでも、GROUP BYとにかく大部分の作業を超高速で行うことができます。

于 2008-11-17T22:38:25.290 に答える
0
select s.*
from Measurement s
where exists ( 
   select 1
   from Measurement s1
   where s.Town = s1.Town
   group by s1.Town
   having max( s1.Date )= s.Date)
   order by s.Town
于 2008-11-17T19:08:52.343 に答える
0

町の個別のリストを含むテーブルを持っている可能性がありますか? 町ごとに約 1000 の測定値があるとすると、ウィンドウ関数ソリューション (row_number()、rank() など) は、通常の集計またはこの APPLY バージョンほどには機能しない可能性があります。

SELECT
   M.*
FROM
   Towns T
   OUTER APPLY (
      SELECT TOP 1 * -- add 'WITH TIES' to the 'TOP 1' if you have/want ties on date.
      FROM Measurement M
      WHERE T.Town = M.Town
      ORDER BY M.Date DESC
   ) M

町のリストがない場合は、これを試すことができますが、単純なバニラ集計 + ルックアップに対してどのように積み重なるかはわかりません。

SELECT
   M.*
FROM
   (SELECT DISTINCT Town FROM Towns) T
   OUTER APPLY (
      SELECT TOP 1 *
      FROM Measurement M
      WHERE T.Town = M.Town
      ORDER BY M.Date DESC
   ) M

これらのクエリのパフォーマンスは、インデックスに完全に依存します。最低でも【町】に1つ必要で、代わりに【町、日付】がベストでしょう。他のテーブルが MeasurementID を使用しているが、MeasurementID を使用して Measurement テーブルにほとんどアクセスしない場合は、クラスター化インデックスを削除し、MeasurementID を非クラスター化 PK にして、Town, Date に (一意ではない) クラスター化インデックスを追加します。MeasurementID を使用する他のテーブルがない場合は、その列を完全に削除します。その場合、理由もなくテーブルを肥大化させる役に立たない合成/人工キーです。

インデックスのこれらの推奨される変更は、集計または APPLY を使用して、ここでの回答のすべてのクエリに役立ちます。ウィンドウ関数への影響についてはわかりませんが、オプティマイザが実行計画をどのように解決するかによって異なります (最大日付にアクセスするだけで他のすべての行には触れないことを認識するのに十分賢い場合、同じインデックスがそれを後押しします)信じられないほどですが、オプティマイザがこれを実行できるとは思えません)。

また、パフォーマンスを向上させるために、町全体を配置する代わりに、TownID を使用して、Town テーブルを使用することをお勧めします。町の名前が変わったら?各名前の平均 15 バイト程度から int TownID のわずか 4 バイトに切り替えると、速度が向上します。(テストはこれを確実に証明するためのものですが)。

于 2012-02-12T00:25:36.827 に答える
0
select m.town, m.temperature, m.date
from Measurement m
where m.date = (select max(m2.date) from Measurement m2 where m2.town = m.town)
order by 1
于 2008-11-17T19:26:40.933 に答える