【问题标题】:MariaDB 10.3.18 : How to get 2 records with random and distinct value?MariaDB 10.3.18:如何获得 2 条具有随机和不同值的记录?
【发布时间】:2020-01-29 11:05:17
【问题描述】:

有一个名为 stat 的 MySQL 表:

line_name  device_name count
1001    3548001  2
1002    3548002  3
1003    3548003  6
2001    3548004  7
2002    3548005  3
2003    3548006  4
3001    3548007  3
3002    3548008  9
3003    3548009  7

我需要选择line_name 中第一个字符不同的两条记录 例如:

1001    3548001  2
3003    3548009  7

或:

2002    3548005  3
3001    3548007  3

我试过了:

SELECT DISTINCT(SUBSTRING(line_name,1,LENGTH(line_name)-3)) as pack_id, device_name, count 
from stat
order by rand()
limit 2;

但有时我会在结果集中得到相同的pack_id

【问题讨论】:

  • 另外,很明显这个查询不能返回那个结果!
  • 草莓,是的,你说得对。我编辑了我的问题
  • distinct 不是函数,它适用于整行。
  • 请提供SHOW CREATE TABLE。我想知道的一件事是line_name 是否“独一无二”。

标签: mysql sql mariadb groupwise-maximum


【解决方案1】:

在 MariaDB 10.3 中,您可以使用 ROW_NUMBER() OVER (ORDER BY RAND()) 为每个不同的 line_name 生成随机行号,然后选择行号 = 1 的随机值对:

WITH cte AS 
(SELECT *, ROW_NUMBER() OVER (PARTITION BY LEFT(line_name, 1) ORDER BY RAND()) AS rn
 FROM stat)
SELECT `line_name`, `device_name`, `count`
FROM cte
WHERE rn = 1
ORDER BY RAND()
LIMIT 2

Demo on dbfiddle

输出(几次运行)

line_name   device_name count
1003        3548003     6
3002        3548008     9

line_name   device_name count
2001        3548004     7
1003        3548003     6

【讨论】:

  • (我认为结尾的LIMIT 2 是不必要的。)
  • @RickJames 鉴于LEFT(line_name, 1) 可以取值(基于相同的数据)1、2 或 3,CTE 中将有 3 行具有 rn = 1。见dbfiddle.uk/…
  • 可爱,但不要指望性能。对于那仅有的 9 行表,38 Handler_read_rnd 34 Handler_read_rnd_next 21 Handler_tmp_write 9 Handler_update 10 Innodb_buffer_pool_read_requests 9 Innodb_rows_read
【解决方案2】:

在 MySQL 8.0 中,您可以在 CTE 中自连接表以找到满足条件的随机记录对,然后使用 UNION ALL 对结果进行反透视:

WITH cte AS (
    SELECT 
        t1.line_name line_name1,
        t1.device_name device_name1,
        t1.count count1,
        t2.line_name line_name2,
        t2.device_name device_name2,
        t2.count count2
    FROM stat t1
    INNER JOIN stat t2 ON LEFT(t1.line_name, 1) != LEFT(t2.line_name, 1)
    ORDER BY RAND()
    LIMIT 1
)
SELECT line_name1, device_name1, count1 FROM cte
UNION ALL
SELECT line_name2, device_name2, count2 FROM cte

Demo on DB Fiddle

运行#1:

| line_name1 | device_name1 | count1 |
| ---------- | ------------ | ------ |
| 3001       | 3548007      | 3      |
| 2001       | 3548004      | 7      |

运行#2:

| line_name1 | device_name1 | count1 |
| ---------- | ------------ | ------ |
| 1003       | 3548003      | 6      |
| 2002       | 3548005      | 3      |

【讨论】:

    【解决方案3】:

    您可以GROUP BY pack_id 并随机选择相应的设备名称或ANY_VALUE() 如果您使用的是 MySQL >= 5.7

    SELECT 
      SUBSTR(line_name, 1, 1) AS pack_id,
      line_name,
      ANY_VALUE(device_name) AS device_name,
      count
    FROM stat
    GROUP BY pack_id
    ORDER BY RAND()
    LIMIT 2
    

    旧的 MySQL 版本

    SELECT 
      SUBSTR(line_name, 1, 1) AS pack_id,
      line_name,
      device_name,
      count
    FROM stat
    GROUP BY pack_id
    ORDER BY RAND()
    LIMIT 2
    

    http://sqlfiddle.com/#!9/2d466f2/1

    请注意,我还简化了 pack_id 的计算

    【讨论】:

    • 虽然方法很好,但是这个答案会将 device_name 限制为每个 line_name 的最大值。然而 OP 需要这一切都是随机的。
    【解决方案4】:

    我会这样做:

    select s.*
    from stat s
    order by row_number() over (partition by left(line_name, 1)
                                order by rand()
                               )
    limit 2;
    

    不需要子查询,因为order by 中允许使用窗口函数。

    这可能不是最有效的方法。但除非你的表很大,否则性能应该还可以。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-03
      • 2015-04-27
      • 1970-01-01
      • 2021-09-23
      相关资源
      最近更新 更多