web-dev-qa-db-ja.com

テーブルをクエリして、他のテーブルのTOP 1関連レコードと結合する方法が必要

この質問 のフォローアップとして、問題のデータを収集するためのより良い、および/またはより効率的な方法があるかどうか疑問に思っています。

前述のように、このクエリは必要なデータの95%を返します-

SELECT dv.Name
      ,MAX(hb.[DateEntered]) as DE
FROM 
    [Devices] as dv
    INNER JOIN 
    [Heartbeats] as hb ON hb.DeviceID = dv.ID
GROUP BY dv.Name
HAVING MAX(hb.[DateEntered]) < '20130304';

同じ結果を達成する方法はありますか(デバイスごとに、DateEnteredによってDESCで注文されたTOPハートビートを選択します)、さらに行全体を選択します[Heartbeats]テーブルから?現在、その行のDateTimeのみを取得しています。

GROUP BY句に追加の列を含めると、それらをselectに追加できます。しかし、私は不要な[Devices]行ごとに複数の行を取得します。奇妙に聞こえますが、私が基本的にやりたいのは、[Devices]に対してクエリを実行し、そのセットに対してfor...eachを実行して、その[Heartbeats]行を追加することです[Devices]行。それは可能ですか?

updateこれはハートビートテーブルの構造です。

CREATE TABLE [dbo].[Heartbeats](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [DeviceID] [int] NOT NULL,
    [IsFMSFMPUp] [bit] NOT NULL,
    [IsFMSWebUp] [bit] NOT NULL,
    [IsPingUp] [bit] NOT NULL,
    [DateEntered] [datetime] NOT NULL,
 CONSTRAINT [PK_Heartbeats] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [CommonQueryIndex] ON [dbo].[Heartbeats] 
(
    [DateEntered] ASC,
    [DeviceID] ASC
)
INCLUDE ( [ID],
[IsFMSFMPUp],
[IsFMSWebUp],
[IsPingUp]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [HeartbeatDeviceIndex] ON [dbo].[Heartbeats] 
(
    [DeviceID] ASC
)
INCLUDE ( [ID]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO
/****** Object:  Default [DF_Heartbeats_DateEntered]    Script Date: 03/05/2013 10:45:45 ******/
ALTER TABLE [dbo].[Heartbeats] ADD  CONSTRAINT [DF_Heartbeats_DateEntered]  DEFAULT (getdate()) FOR [DateEntered]
GO
/****** Object:  ForeignKey [FK_Heartbeats_Devices]    Script Date: 03/05/2013 10:45:45 ******/
ALTER TABLE [dbo].[Heartbeats]  WITH CHECK ADD  CONSTRAINT [FK_Heartbeats_Devices] FOREIGN KEY([DeviceID])
REFERENCES [dbo].[Devices] ([ID])
GO
ALTER TABLE [dbo].[Heartbeats] CHECK CONSTRAINT [FK_Heartbeats_Devices]
GO
3
Nate

_OUTER APPLY_を使用すると、これをかなり簡単に実行できます(2005以降の場合)。 ROW_NUMBER()を使用するなど、結果を達成するためのより良いパフォーマンスの方法があることに注意してください-疑わしい場合は実行プランを確認してください。また、_SELECT *_は遅延してお勧めできません。ここでは、説明のためにこれを実行しています。ハートビートテーブルの実際の構造がわからないためです。

_SELECT
    dv.Name,
    hb.*
FROM [Devices] as dv
    OUTER APPLY (
        SELECT TOP 1 *
        FROM Heartbeats
        WHERE Heartbeats.DeviceID = dv.ID
        ORDER BY DateEntered DESC
    ) hb
WHERE ISNULL(hb.DateEntered, '1900-01-01T00:00') < '2013-03-04T00:00'
_

_OUTER APPLY_と_CROSS APPLY_の細かい点については、Books Onlineを参照してください(これは_OUTER JOIN_と_INNER JOIN_によく似ています)。 SQL Server 2000でこのようにクエリを実行するのは常に大変で、_OUTER/CROSS APPLY_ orROW_NUMBER()関数がありませんでした。

5
db2

このタイプの「自己結合」を使用してみてください。

select dat.*
from 
(SELECT dv.Name,MAX(hb.[DateEntered]) as DE
FROM [Devices] as dv
    INNER JOIN [Heartbeats] as hb ON hb.DeviceID = dv.ID
GROUP BY dv.Name
HAVING MAX(hb.[DateEntered]) < '20130304') grp 
    inner join 
(SELECT dv.Name, hb.*
FROM [Devices] as dv
    INNER JOIN [Heartbeats] as hb ON hb.DeviceID = dv.ID) dat 
        on grp.name = dat.name and grp.DE = dat.[DateEntered]
0
RoKa