1

各ポータルから1行 (日付ごとの最後の行)だけを選択しようとしていますが、group by/distinct で問題が発生しています

このコードを使用すると、必要な portalId のみを選択できますが、データはありません

Select relNews.PortalId
from news
left join relNews on relNews.NewsId= news.NewsId
group by relNews.PortalId

このコードのように 1 つまたは複数のデータ列を追加すると、選択により、ポータルごとに 1 つだけでなく、すべての情報が表示されます

Select relNews.PortalId, news.NewsId
from news
left join relNews on relNews.NewsId= news.NewsId
group by relNews.PortalId, news.NewsId

それは私がここで見逃している小さなトリックであることは知っていますが、何が思い出せないのですか...

アップデート

この例の仮想テーブルを作成しましょう。表は(ここnewsrelNewsはできるだけ短くしました)

テーブルニュース

  • ニュースID
  • 題名
  • 説明
  • 日にち

テーブル relNews

  • RelNewsId
  • ニュースID
  • ポータル ID

ノート:

  • relNews はN同じ NewsId のレジスタを持つことができます
  • (news.Date に基づいて) portalId ごとに最後の登録を選択する必要があります。

まあ言ってみれば:

テーブルニュース

  • ニュース ID == 1
  • タイトル == 'テスト'
  • 説明 == 'テスト'
  • 日付 == '2013-01-01 00:00:00'

  • ニュース ID == 2

  • タイトル == 'test2'
  • 説明 == 'test2'
  • 日付 == '2013-01-01 03:00:00'

  • ニュース ID == 3

  • タイトル == 'test3'
  • 説明 == 'test3'
  • 日付 == '2013-01-02 00:00:00'

テーブル relNews

  • RelNewsId == 1
  • ニュース ID == 1
  • ポータル ID == 1

  • RelNewsId == 2

  • ニュース ID == 1
  • ポータル ID == 2

  • RelNewsId == 3

  • ニュース ID == 2
  • ポータル ID == 1

  • RelNewsId == 4

  • ニュース ID == 3
  • ポータル ID == 3

このデータは次のことをもたらすはずです。

RelNewsId == 2 ; RelNewsId == 3 ; RelNewsId == 4 ;


このコードで必要な結果を得ることができます:

Select top 1 relNews.PortalId, news.NewsId, news.date
from news
left join relNews on relNews.NewsId= news.NewsId
where relNews.PortalId == 1
group by relNews.PortalId, news.NewsId
order by news.date desc
UNION
Select top 1 relNews.PortalId, news.NewsId, news.date
from news
left join relNews on relNews.NewsId= news.NewsId
where relNews.PortalId == 2
group by relNews.PortalId, news.NewsId
order by news.date desc
UNION
Select top 1 relNews.PortalId, news.NewsId, news.date
from news
left join relNews on relNews.NewsId= news.NewsId
where relNews.PortalId == 3
group by relNews.PortalId, news.NewsId
order by news.date desc

次に、3つの結果すべてを取得します。

4

4 に答える 4

4

1 つの行に複数の行が存在する可能性がある場合は、どの行が必要か示す何らかの方法を提供する必要があります。あなたが発見したように、各親に対して多くの値を持つことができる列を作成すると、複数の行が得られます。これを行うにはいくつかの方法があります。SQL Server 2008 を使用しているため、いくつかのオプションがあります。newsrelNewsGROUP BY

  1. CROSS/OUTER APPLY -一致する行がない場合に行を除外する場合は、をに変更OUTER APPLYします。CROSS APPLYrelNewsNews

    SELECT
       R.Whatever,
       N.Whatever
    FROM
       dbo.relNews R
       OUTER APPLY (
          SELECT TOP 1 *
          FROM dbo.News N
          WHERE R.newsId = N.Id
          ORDER BY N.newsDate DESC
       ) N
    
  2. 行番号()

    SELECT
       R.Whatever,
       N.Whatever
    FROM
       dbo.RelNews R
       LEFT JOIN
          (
             SELECT
                *,
                Selector = Row_Number()
                   OVER (PARTITION BY N.Id ORDER BY N.newsDate DESC),
             FROM dbo.News N
          ) N ON R.newsId = N.Id
          AND N.Selector = 1
    
  3. Aggregate - これは必要と思われるよりも複雑ですが、newsDateごとに一意ではないと仮定しましたnewsID。一意であれば、より簡単です。このバージョンは SQL 2000 で動作します。また、これはおそらく、私が提供するすべてのオプションの中で最もパフォーマンスの悪いクエリでもあります。UniqueColumnは、 ごとnewsIDに一意の値が保証されている任意の列であり、 の同順位の中から選択するために使用できることに注意してくださいnewsDate

    SELECT
       R.Whatever,
       N.Whatever
    FROM
       dbo.RelNews R
       LEFT JOIN (
          dbo.News N
          INNER JOIN (
             SELECT N.Id, MaxUnique = Max(UniqueColumn)
             FROM
                dbo.News N
                INNER JOIN (
                   SELECT N.Id, MaxDate = Max(N.newsDate)
                   FROM dbo.News N
                   GROUP BY N.Id
                ) X ON N.Id = X.Id
                AND N.newsDate = X.MaxDate
             GROUP BY N.Id
          ) X ON N.Id = X.Id
          AND X.UniqueColumn = X.MaxUnique
       ) ON R.newsId = N.Id
    

    newsDateが本当に一意である場合news.Id、そのクエリは次のとおりです。

    SELECT
       R.Whatever,
       N.Whatever
    FROM
       dbo.RelNews R
       LEFT JOIN (
          dbo.News N
          INNER JOIN (
             SELECT N.Id, MaxDate = Max(N.newsDate)
             FROM dbo.News N
             GROUP BY N.Id
          ) X ON N.Id = X.Id
          AND N.newsDate = X.MaxDate
       ) ON R.newsId = N.Id
    
  4. サブクエリ- 一度に 1 つの列しか取得できないという問題がありますが、SQL 2000 では機能し、適切なインデックスでうまく機能するはずです。複数の列の場合、各列に対して個別のクエリを実行する可能性があるため、パフォーマンスが低下する可能性があります。

    SELECT
       R.Whatever,
       NWhatever = (
          SELECT TOP 1 N.Whatever
          FROM dbo.News N
          WHERE R.newsId = N.Id
          ORDER BY N.newsDate DESC
       )
    FROM
       dbo.relNews R
    
  5. ON 句のサブクエリ- 複数の列をプルする場合は、おそらく SQL 2000 に最適なクエリです。News テーブルに 2 回ヒットする必要がありますが、適切なインデックスがあればそれほど悪くはありません。UniqueColumnは、 ごとnewsIDに一意の値が保証されている任意の列であり、 の同順位の中から選択するために使用できることに注意してくださいnewsDate

    SELECT
       R.Whatever,
       N.Whatever
    FROM
       dbo.relNews R
       LEFT JOIN dbo.News N
          ON R.newsId = N.Id
             -- above condition not absolutely required logically,
             -- but definitely for indexes
          AND N.UniqueColumn = (
             SELECT TOP 1 N.UniqueColumn
             FROM dbo.News N
             WHERE R.newsId = N.Id
             ORDER BY N.newsDate DESC
          )
    
  6. Logical-Last - SQL 2000 のもう 1 つのおそらく優れたパフォーマーです。これはfo_x86と同じ論理クエリですが、表現が少し異なります。

    SELECT
       R.Whatever,
       N.Whatever
    FROM
       dbo.relNews R
       LEFT JOIN dbo.News N
          ON R.newsId = N.Id
          AND NOT EXISTS (
             SELECT *
             FROM dbo.News X
             WHERE
                N.Id = X.Id
                AND (
                   N.newsDate < X.newsDate
                   OR (
                      N.newsDate = X.newsDate
                      AND N.UniqueColumn < X.UniqueColumn
                   )
                )
          )
    

使用しているデータベースのバージョンでは、おそらくオプション #1 が最適です。

最後の注意: いつものように、テストが必要です。これらすべてのさまざまなクエリのパフォーマンスは、データのパターン (各ニュース項目の日付が多いか少ないか)、正確なインデックス、テーブルの幅、および条件を追加するかどうか (たとえば、 、私が提案した行番号1は、外部relNewsテーブルの条件が数行しか返さない場合はうまく機能しません)。あるクエリで十分な実行時間が得られない場合は、別のクエリを試してください。

于 2012-12-28T19:46:16.650 に答える
1

newsid が順次作成され、「ニュース」が「アイテム」であると仮定すると、次のようになります。

Select relNews.PortalId, max(relnews.newsid) as MostRecentNews
from relNews
group by relNews.PortalId

IDだけが必要な場合は、参加する必要はありません。

于 2012-12-28T19:42:46.900 に答える
1

portal-id を日付でランク付けします

 select relNews.PortalId
 from news
 left join relNews on relNews.newsId = news.Id
 -- select rows for which there is no greater date than this row's date
 left outer join relNews2 on relNews.newsId = relNews2.newsId and relNews.date < relNews2.date
 where relNews2.PortalId is null
 group by relNews.PortalId, relNews.newsId
于 2012-12-28T19:43:51.017 に答える
0

最高のパフォーマンスを実現する最も簡単で最短の方法は、次のように ROW_NUMBER() Over(Partition by ...) を使用することです。

SELECT TOP 20 News.*, 
ROW_NUMBER() Over(Partition by relNews.PortalId Order By news.Id DESC ) R
FROM news LEFT JOIN relNews on relNews.newsId = news.Id
ORDER BY R

ここではトップ 20 を使用します。これは、各ポータルの最初のニュースを表示するポータル数であるためです。

これは、「Partition By」の使用法を示し、表現するための最良の例です。

于 2012-12-28T21:16:47.970 に答える