web-dev-qa-db-ja.com

SQLクエリでグループごとに最初の行を選択するにはどうすればよいですか?

私はこのSQLクエリを持っています:

SELECT   Foo, Bar, SUM(Values) AS Sum
FROM     SomeTable
GROUP BY Foo, Bar
ORDER BY Foo DESC, Sum DESC

これにより、次のような出力が得られます。

47  1   100
47  0   10
47  2   10
46  0   100
46  1   10
46  2   10
44  0   2

Fooごとに最初の行だけを作成し、残りは無視したいと思います。

47  1   100
46  0   100
44  0   2

それ、どうやったら出来るの?

13
mafu
declare @sometable table ( foo int, bar int, value int )

insert into @sometable values (47, 1, 100)
insert into @sometable values (47, 0, 10)
insert into @sometable values (47, 2, 10)
insert into @sometable values (46, 0, 100)
insert into @sometable values (46, 1, 10)
insert into @sometable values (46, 2, 10)
insert into @sometable values (44, 0, 2)

;WITH cte AS 
(
    SELECT   Foo, Bar, SUM(value) AS SumValue, ROW_NUMBER() OVER(PARTITION BY Foo ORDER BY FOO DESC, SUM(value) DESC) AS RowNumber
    FROM     @SomeTable
    GROUP BY Foo, Bar
)
SELECT * 
FROM cte
WHERE RowNumber = 1
26
Anton Georgiev

Ansiiスタイルの結合を使用すると、サブクエリよりも読みやすくなることが多いという点で、rjmunruに同意できないかもしれませんが、それぞれに、DBAの指示に従います。

クエリの最初の結果だけが必要な場合は、rownumを使用できる可能性があります(Oracleを使用している場合、他のデータベースにも同様の機能があります)。

select * from foo_t f where f.bar = 'bleh' and rownum = 1

もちろん、何をしようとしているのかによっては、HAVING句も適切な場合があります。

「HAVINGは、基本的なSQLステートメントの行に対するWHERE句と同様に、GROUP BYによって作成されたグループに対してアクションを実行するために使用されます。WHERE句は、評価される行を制限します。HAVING句は、返されるグループ化された行を制限します。」

hth

2
tmeisenh

古い投稿ですが、今日も同じ問題がありました。それが機能するまで多くのクエリを試すことでそれを解決しました。 Visual Basic2010でSQLCompact3.5を使用しています。

この例は、列「Id」(主キー)、「nom」(名前)、および「value」を持つ「TESTMAX」という名前のテーブルの場合です。これを使用して、各「nom」の最大「value」を持つ行を取得できます。

SELECT TESTMAX.Id, TESTMAX.NOM, TESTMAX.Value
FROM     TESTMAX INNER JOIN
                  TESTMAX AS TESTMAX_1 ON TESTMAX.NOM = TESTMAX_1.NOM
WHERE  (TESTMAX.Value IN
                      (SELECT MAX(Value) AS Expr1
                       FROM      TESTMAX AS TESTMAX_2
                       WHERE   (NOM = TESTMAX_1.NOM)))
GROUP BY TESTMAX.Id, TESTMAX.NOM, TESTMAX.Value

他の行を削​​除する場合は、次を使用することもできます。

DELETE FROM TESTMAX
WHERE  (Id NOT IN
                      (SELECT TESTMAX_3.Id
                       FROM      TESTMAX AS TESTMAX_3 INNER JOIN
                                         TESTMAX AS TESTMAX_1 ON TESTMAX_3.NOM = TESTMAX_1.NOM
                       WHERE   (TESTMAX_3.Value IN
                                             (SELECT MAX(Value) AS Expr1
                                              FROM      TESTMAX AS TESTMAX_2
                                              WHERE   (NOM = TESTMAX_1.NOM)))
                       GROUP BY TESTMAX_3.Id, TESTMAX_3.NOM, TESTMAX_3.Value))
1
Gerauld

Players.Nickだけでグループ化し、説明の最初(分)を選択するだけです

SELECT     Players.Nick, MIN(Reasons.Description), SUM(Marks.Value) AS Sum
FROM         Marks INNER JOIN
                      Players ON Marks.PlayerID = Players.ID INNER JOIN
                      Reasons ON Marks.ReasonId = Reasons.ID
GROUP BY Players.Nick
ORDER BY Players.Nick, Sum DESC

それはあなたがそれを知らずに常に最初のものが欲しい場合です

1
Peter

一般に、結合してグループ化するのではなく、サブクエリを使用してみてください。SQLがはるかに理解しやすくなることがよくあります。

SELECT Nick,
   (SELECT Description from Reasons WHERE Reasons.ID = (
       SELECT FIRST(Marks.ReasonId) from Marks WHERE Marks.PlayerID = Players.ID)
   ),
   (SELECT SUM(Value) from Marks WHERE Marks.PlayerID = Players.ID)
1
rjmunro

これは「HAVING」句を使用する機会ですか? (集計関数で区別したい-'合計')?

0
monojohnny

(編集された質問に基づいて編集されました)次に、集約された列の値に基づいてフィルタリングしたいので、必要なのはHaving句です。

  SELECT p.Nick, r.Description, SUM(m.Value) Sum
  FROM Marks m
    JOIN Players p
      ON m.PlayerID = p.ID 
    JOIN Reasons r 
      ON m.ReasonId = r.ID
  GROUP BY p.Nick, r.Description
  Having SUM(m.Value) =
      (Select Max(Sum) From
        (SELECT SUM(m.Value) Sum
         FROM Marks mi
           JOIN Players pi
              ON mi.PlayerID = pi.ID 
           JOIN Reasons r i
             ON mi.ReasonId = ri.ID
         Where pi.Nick = p.Nick
         GROUP BY pi.Nick, ri.Description))

  Order By p.Nick, Sum Desc
0
Charles Bretana

SQL Server2005ではこれを使用できます。

@sometableテーブルを宣言します(foo int、bar int、value int)

@sometable値(1、5、10)に挿入@sometable値(1、4、20)に挿入@sometable値(2、1、1)に挿入@sometable値(2、1、10)に挿入@sometable値(2、1、1)@sometable値(2、2、13)に挿入@sometable値(3、4、25)に挿入@sometable値(3、5、1)に挿入@sometableに挿入値(3、1、1)@sometable値に挿入(3、1、1)@sometable値に挿入(3、1、1)@sometable値に挿入(3、1、1)@sometable値に挿入( 3、1、1)

-初期集計用の一時テーブルdeclare @ t2 table(foo int、bar int、sums int)@ t2に挿入select foo、bar、sum(value)from @sometable group by foo、bar

select * from(SELECT foo、bar、sums、ROW_NUMBER()OVER(PARTITION BY Foo ORDER BY Sums DESC)ROWNO FROM @ t2)x where x.ROWNO = 1

0
Vinh

奇妙な。これを機能させる唯一の方法は、メモリ内の一時保持テーブルを使用することでした。 (TSQL構文)

-- original test data
declare @sometable table ( foo int, bar int, value int )

insert into @sometable values (1, 5, 10)
insert into @sometable values (1, 4, 20)
insert into @sometable values (2, 1, 1)
insert into @sometable values (2, 1, 10)
insert into @sometable values (2, 1, 1)
insert into @sometable values (2, 2, 13)
insert into @sometable values (3, 4, 25)
insert into @sometable values (3, 5, 1)
insert into @sometable values (3, 1, 1)
insert into @sometable values (3, 1, 1)
insert into @sometable values (3, 1, 1)
insert into @sometable values (3, 1, 1)
insert into @sometable values (3, 1, 1)

-- temp table for initial aggregation
declare @t2 table (foo int, bar int, sums int)
insert into @t2
select foo, bar, sum(value) 
from @sometable
group by foo, bar

-- final result
select foo, bar, sums
from @t2 a
where sums = 
    (select max(sums) from @t2 b 
     where b.foo = a.foo)
0
Kilanash