1

SQL Server 2008/TSQL で何らかのループを実行しようとしていますが、これが aWHILEまたはCURSOR両方である必要があるかどうかわかりません。最終結果は、ユーザー ログインのリストをループしてから、一意のユーザーを特定し、ループを実行して、ユーザーがサイトに 5 分間滞在するのにかかった訪問回数をチャネルごとに決定しようとしています。 .

表: ログイン履歴

UserID  Channel   DateTime          DurationInSeconds
1       Website   1/1/2013 1:13PM   170
2       Mobile    1/1/2013 2:10PM   60
3       Website   1/1/2013 3:10PM   180
4       Website   1/1/2013 3:20PM   280
5       Website   1/1/2013 5:00PM   60
1       Website   1/1/2013 5:05PM   500
3       Website   1/1/2013 5:45PM   120
1       Mobile    1/1/2013 6:00PM   30
2       Mobile    1/1/2013 6:10PM   90
5       Mobile    1/1/2013 7:30PM   400
3       Website   1/1/2013 8:00PM   30
1       Mobile    1/1/2013 9:30PM   200

このスキーマに対する SQL フィドル

select次のように、一意のユーザーを新しいテーブルに入れることができます。

SELECT UserID
INTO #Users
FROM LoginHistory
GROUP BY UserID

今、私が開発しようとしている機能は、これらの一意の UserID をループし、DateTime でログインを並べ替え、300 秒に到達するために必要なログイン数をカウントすることです。

取得したい結果セットは次のようになります。

UserID  TotalLogins  WebsiteLogins  MobileLogins    Loginsneededto5Min
1       4            2              2               2
2       2            2              0               0   
3       3            3              0               3
4       1            1              0               0
5       2            1              1               2

これを別の言語で実行していたら、次のようになると思います。

for (i in #Users):
  TotalLogins = Count(*), 
  WebsiteLogins = Count(*) WHERE Channel = 'Website', 
  MobileLogins = Count(*) WHERE Channel = 'Mobile', 
    for (i in LoginHistory):
      if Duration < 300:
        count(NumLogins) + 1

**わかりました-複数の異なる言語/構文を組み合わせた方法で自分自身を笑っていますが、これが私がこれを解決する方法を考えている方法です**

これを達成するための良い方法について考えていますか?if/then私の好みは、コードにロジック を書き続けることができるように、ループを使用することです。

4

2 に答える 2

0

少し異なるピースごとのアプローチ。わずかな違いは、使用可能なすべてのログインを合計するのではなく、各ユーザーが 300 秒に達したときに再帰部分が終了することです。

UserId/のインデックスはStartTime、より大きなデータセットでのパフォーマンスを向上させるはずです。

declare @Logins as Table ( UserId Int, Channel VarChar(10), StartTime DateTime, DurationInSeconds Int )
insert into @Logins ( UserId, Channel, StartTime, DurationInSeconds ) values
  ( 1, 'Website', '1/1/2013 1:13PM', 170 ),
  ( 2, 'Mobile', '1/1/2013 2:10PM', 60 ),
  ( 3, 'Website', '1/1/2013 3:10PM', 180 ),
  ( 4, 'Website', '1/1/2013 3:20PM', 280 ),
  ( 5, 'Website', '1/1/2013 5:00PM', 60 ),
  ( 1, 'Website', '1/1/2013 5:05PM', 500 ),
  ( 3, 'Website', '1/1/2013 5:45PM', 120 ),
  ( 1, 'Mobile', '1/1/2013 6:00PM', 30 ),
  ( 2, 'Mobile', '1/1/2013 6:10PM', 90 ),
  ( 5, 'Mobile', '1/1/2013 7:30PM', 400 ),
  ( 3, 'Website', '1/1/2013 8:00PM', 30 ),
  ( 1, 'Mobile', '1/1/2013 9:30PM', 200 )

select * from @Logins

; with MostRecentLogins as (
  -- Logins with flags for channel and sequenced by   StartTime   (ascending) for each   UserId .
  select UserId, Channel, StartTime, DurationInSeconds,
    case when Channel = 'Website' then 1 else 0 end as WebsiteLogin,
    case when Channel = 'Mobile' then 1 else 0 end as MobileLogin,
    Row_Number() over ( partition by UserId order by StartTime ) as Seq
    from @Logins ),
  CumulativeDuration as (
  -- Start with the first login for each   UserId .
  select UserId, Seq, DurationInSeconds as CumulativeDurationInSeconds
    from MostRecentLogins
    where Seq = 1
  union all
  -- Accumulate additional logins for each   UserId   until the running total exceeds 300 or they run out of logins.
  select CD.UserId, MRL.Seq, CD.CumulativeDurationInSeconds + MRL.DurationInSeconds
    from CumulativeDuration as CD inner join
      MostRecentLogins as MRL on MRL.UserId = CD.UserId and MRL.Seq = CD.Seq + 1 and CD.CumulativeDurationInSeconds < 300 )
  -- Display the summary.
  select UserId, Sum( WebsiteLogin + MobileLogin ) as TotalLogins,
    Sum( WebsiteLogin ) as WebsiteLogins, Sum( MobileLogin ) as MobileLogins,
    ( select Max( Seq ) from CumulativeDuration where UserId = LT3.UserId and CumulativeDurationInSeconds >= 300 ) as LoginsNeededTo5Min
    from MostRecentLogins as LT3
    group by UserId
    order by UserId

サンプル結果にエラーがあるように見えることに注意してください。 UserId3 は 2 回の呼び出しで 300 秒に達します: 180 + 120。あなたの例は 3 回の呼び出しを示しています。

于 2013-02-13T04:30:13.497 に答える
0

CURSORわかりました。これは、セットベースのソリューションよりもパフォーマンスが優れている可能性が高い時期の 1 つです。悲しいことに、私はカーソルがあまり得意ではないので、試してみるための一連の基本的な解決策を提供できます。

;WITH CTE AS
(
    SELECT *, ROW_NUMBER() OVER(PARTITION BY UserID ORDER BY [DateTime]) RN
    FROM UserLogins
), CTE2 AS
(
    SELECT  *, 1 RecursionLevel
    FROM CTE
    WHERE RN = 1
    UNION ALL
    SELECT  B.UserID, B.Channel, B.[DateTime], 
            A.DurationInSeconds+B.DurationInSeconds, 
            B.RN, RecursionLevel+1
    FROM CTE2 A
    INNER JOIN CTE B
        ON A.UserID = B.UserID AND A.RN = B.RN - 1
)
SELECT  A.UserID,
        COUNT(*) TotalLogins,
        SUM(CASE WHEN Channel = 'Website' THEN 1 ELSE 0 END) WebsiteLogins,
        SUM(CASE WHEN Channel = 'Mobile' THEN 1 ELSE 0 END) MobileLogins,
        ISNULL(MIN(RecursionLevel),0) LoginsNeedeto5Min
FROM UserLogins A
LEFT JOIN ( SELECT UserID, MIN(RecursionLevel) RecursionLevel
            FROM CTE2 
            WHERE DurationInSeconds > 300
            GROUP BY UserID) B
    ON A.UserID = B.UserID
GROUP BY A.UserID
于 2013-02-12T21:40:22.007 に答える