簡體   English   中英

select MySQL中每個組的第一行如何?

[英]How to select the first row for each group in MySQL?

在 C# 中會是這樣的:

table
   .GroupBy(row => row.SomeColumn)
   .Select(group => group
       .OrderBy(row => row.AnotherColumn)
       .First()
   )

Linq-To-Sql 將其轉換為以下 T-SQL 代碼:

SELECT [t3].[AnotherColumn], [t3].[SomeColumn]
FROM (
    SELECT [t0].[SomeColumn]
    FROM [Table] AS [t0]
    GROUP BY [t0].[SomeColumn]
    ) AS [t1]
OUTER APPLY (
    SELECT TOP (1) [t2].[AnotherColumn], [t2].[SomeColumn]
    FROM [Table] AS [t2]
    WHERE (([t1].[SomeColumn] IS NULL) AND ([t2].[SomeColumn] IS NULL))
      OR (([t1].[SomeColumn] IS NOT NULL) AND ([t2].[SomeColumn] IS NOT NULL)
        AND ([t1].[SomeColumn] = [t2].[SomeColumn]))
    ORDER BY [t2].[AnotherColumn]
    ) AS [t3]
ORDER BY [t3].[AnotherColumn]

但它與 MySQL 不兼容。

我的回答僅基於您帖子的標題,因為我不懂 C# 並且不理解給定的查詢。 但是在 MySQL 中,我建議您嘗試子選擇。 首先獲取一組有趣列的主鍵,然后從這些行中選擇數據:

SELECT somecolumn, anothercolumn 
  FROM sometable 
 WHERE id IN (
               SELECT min(id) 
                 FROM sometable 
                GROUP BY somecolumn
             );

當我寫

SELECT AnotherColumn
FROM Table
GROUP BY SomeColumn
;

有用。 IIRC 在其他 RDBMS 中這樣的語句是不可能的,因為不屬於分組鍵的列在沒有任何類型的聚合的情況下被引用。

這種“怪癖”與我想要的非常接近。 所以我用它來得到我想要的結果:

SELECT * FROM 
(
 SELECT * FROM `table`
 ORDER BY AnotherColumn
) t1
GROUP BY SomeColumn
;

這是您可以嘗試的另一種方法,不需要該 ID 字段。

select some_column, min(another_column)
  from i_have_a_table
 group by some_column

我仍然同意 lfagundes 您應該添加一些主鍵..

還要注意,通過這樣做,您不能(輕松)獲得其他值與結果 some_colum, another_column 對相同的行! 你需要 lfagundes apprach 和 PK 來做到這一點!

SELECT
    t1.*

FROM
    table_name AS t1

    LEFT JOIN table_name AS t2 ON (
        t2.group_by_column = t1.group_by_column
        -- group_by_column is the column you would use in the GROUP BY statement
        AND
        t2.order_by_column < t1.order_by_column
        -- order_by_column is column you would use in the ORDER BY statement
        -- usually is the autoincremented key column
    )

WHERE
    t2.group_by_column IS NULL;

使用 MySQL v8+,您可以使用窗口函數

您應該使用一些聚合函數來獲取您想要的 AnotherColumn 的值。 也就是說,如果您想要 SomeColumn 的每個值(數字或字典順序)的 AnotherColumn 的最低值,您可以使用:

SELECT SomeColumn, MIN(AnotherColumn)
FROM YourTable
GROUP BY SomeColumn

一些希望有用的鏈接:

http://dev.mysql.com/doc/refman/5.1/en/group-by-functions.html

http://www.oreillynet.com/databases/blog/2007/05/debunking_group_by_myths.html

來自MySQL 5.7 文檔

MySQL 5.7.5 及更高版本實現了功能依賴檢測。 如果啟用了 ONLY_FULL_GROUP_BY SQL 模式(默認情況下是這樣),MySQL 將拒絕選擇列表、HAVING 條件或 ORDER BY 列表引用非聚合列的查詢,這些列既不在 GROUP BY 子句中命名,也不在功能上依賴它們.

這意味着@Jader Dias 的解決方案不會在任何地方都有效。

這是啟用ONLY_FULL_GROUP_BYONLY_FULL_GROUP_BY的解決方案:

SET @row := NULL;
SELECT
    SomeColumn,
    AnotherColumn
FROM (
    SELECT
        CASE @id <=> SomeColumn AND @row IS NOT NULL 
            WHEN TRUE THEN @row := @row+1 
            ELSE @row := 0 
        END AS rownum,
        @id := SomeColumn AS SomeColumn,
        AnotherColumn
    FROM
        SomeTable
    ORDER BY
        SomeColumn, -AnotherColumn DESC
) _values
WHERE rownum = 0
ORDER BY SomeColumn;

我在答案中沒有看到以下解決方案,所以我想我會把它放在那里。

問題是在按AnotherColumn分組的所有組中按SomeColumn排序時選擇第一行。

以下解決方案將在 MySQL 中執行此操作。 id必須是一個唯一的列,它不能包含包含- (我用作分隔符)的值。

select t1.*
from mytable t1
inner join (
  select SUBSTRING_INDEX(
    GROUP_CONCAT(t3.id ORDER BY t3.AnotherColumn DESC SEPARATOR '-'),
    '-', 
    1
  ) as id
  from mytable t3
  group by t3.SomeColumn
) t2 on t2.id = t1.id


-- Where 
SUBSTRING_INDEX(GROUP_CONCAT(id order by AnotherColumn desc separator '-'), '-', 1)
-- can be seen as:
FIRST(id order by AnotherColumn desc)

-- For completeness sake:
SUBSTRING_INDEX(GROUP_CONCAT(id order by AnotherColumn desc separator '-'), '-', -1)
-- would then be seen as:
LAST(id order by AnotherColumn desc)

在 MySQL 錯誤跟蹤器中有一個對FIRST()LAST()功能請求,但它在很多年前就被關閉了。

最佳性能且易於使用:

SELECT id, code,
SUBSTRING_INDEX( GROUP_CONCAT(price ORDER BY id DESC), ',', 1) first_found_price
FROM stocks
GROUP BY code
ORDER BY id DESC

我建議使用 MySql 中的這種官方方式:

SELECT article, dealer, price
FROM   shop s1
WHERE  price=(SELECT MAX(s2.price)
              FROM shop s2
              WHERE s1.article = s2.article
              GROUP BY s2.article)
ORDER BY article;

通過這種方式,我們可以獲得每篇文章的最高價格

這個怎么樣:

SELECT SUBSTRING_INDEX(
      MIN(CONCAT(OrderColumn, '|', IFNULL(TargetColumn, ''))
    ), '|', -1) as TargetColumn
FROM table
GROUP BY GroupColumn

另一種方法(沒有主鍵)是使用 JSON 函數:

select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") )
  from sometable group by somecolumn

或 5.7.22 之前的版本

select somecolumn, 
  json_unquote( 
    json_extract( 
      concat('["', group_concat(othercolumn separator '","') ,'"]') 
    ,"$[0]" ) 
  ) 
  from sometable group by somecolumn

排序(或過濾)可以在分組之前完成:

select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") ) 
  from (select * from sometable order by othercolumn) as t group by somecolumn

...或分組后(當然):

select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") ) as other 
  from sometable group by somecolumn order by other

不可否認,它相當復雜,性能可能不是很好(沒有在大數據上測試它,在我有限的數據集上運行良好)。

另一種方法來做到這一點

從在視圖中工作的組中選擇最大值

SELECT * FROM action a 
WHERE NOT EXISTS (
   SELECT 1 FROM action a2 
   WHERE a2.user_id = a.user_id 
   AND a2.action_date > a.action_date 
   AND a2.action_type = a.action_type
)
AND a.action_type = "CF"

在 Mysql 中為每個組選擇第一行(按列排序)

我們有:

一張桌子: mytable
我們排序的列: the_column_to_order_by
我們希望分組的列: the_group_by_column

這是我的解決方案。 內部查詢為您提供一組唯一的行,選擇為雙鍵。 外部查詢通過連接這兩個鍵(使用 AND)來連接同一個表。

SELECT * FROM 
    ( 
        SELECT the_group_by_column, MAX(the_column_to_order_by) the_column_to_order_by 
        FROM mytable 
        GROUP BY the_group_by_column 
        ORDER BY MAX(the_column_to_order_by) DESC 
    ) as mytable1 
JOIN mytable mytable2 ON mytable2.the_group_by_column = 
mytablealiamytable2.the_group_by_column 
  AND mytable2.the_column_to_order_by = mytable1.the_column_to_order_by;

僅供參考:我根本沒有考慮過效率問題,也無法以一種或另一種方式談論它。

我最近發現了一個很酷的技巧來實現這一點。 基本上只是從一個表中創建兩個不同的子查詢並將它們連接在一起。 其中一個子查詢基於分組進行聚合,另一個子查詢僅獲取每個分組項的第一個 DISTINCT 行。

當您將這些子查詢連接在一起時,您將從每個組中獲得第一個不同的項目,但也會為每個項目獲得整個組中的聚合列。 這與關閉 ONLY_FULL_GROUP_BY 的結果基本相同。

SELECT non_aggregated_data.foo_column AS foo_column,
       non_aggregated_data.bar_column AS bar_column,
       aggregated_data.value_1_sum    AS value_1_sum,
       aggregated_data.value_2_sum    AS value_2_sum
FROM (SELECT column_to_self_join_on,
             sum(value_1) AS value_1_sum,
             sum(value_2) AS value_2_sum
      FROM example_table
      GROUP BY column_to_self_join_on) AS aggregated_data
         LEFT JOIN (SELECT DISTINCT(column_to_self_join_on),
                                   foo_column,
                                   bar_column
                    FROM example_table) AS non_aggregated_data
                   ON non_aggregated_data.column_to_self_join_on = aggregated_data.column_to_self_join_on

rtribaldos 提到在較年輕的數據庫版本中,可以使用窗口函數
這是一個對我有用的代碼,它與 Martin Zwarík 的substring_index解決方案一樣快(在 Mariadb 10.5.16 中):

SELECT * FROM (
  SELECT group_col, order_col
  , ROW_NUMBER() OVER(PARTITION BY group_col ORDER BY order_col) rnr 
  FROM some_table
  WHERE <some_condition>
  ORDER BY group_col
) i
WHERE rnr=1;

為什么不使用 MySQL LIMIT 關鍵字?

SELECT [t2].[AnotherColumn], [t2].[SomeColumn]
FROM [Table] AS [t2]
WHERE (([t1].[SomeColumn] IS NULL) AND ([t2].[SomeColumn] IS NULL))
  OR (([t1].[SomeColumn] IS NOT NULL) AND ([t2].[SomeColumn] IS NOT NULL)
    AND ([t1].[SomeColumn] = [t2].[SomeColumn]))
ORDER BY [t2].[AnotherColumn]
LIMIT 1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM