【问题标题】:MySQL query for showing only the max value length of each columnMySQL查询仅显示每列的最大值长度
【发布时间】:2017-01-26 00:47:03
【问题描述】:

我有一个 n 列 x 行的表格。

我想进行查询以显示包含所有列的表以及该列的最大长度值(以及该行的最大长度值的行 ID)。

+--------+--------+----+
| column | length | id |
+--------+--------+----+
| a      |    123 |  1 |
| b      |    123 |  8 |
| c      |    123 |  6 |
| d      |    123 |  5 |
| e      |    123 |  3 |
+--------+--------+----+

a、b、c 等是列名,length 是该列中的最大长度,id 是该列的最大值长度的行。

像这样的东西?但随后为所有列动态:

SELECT MAX(LENGTH(`column_a`)) AS `length`, `id`
FROM `table` GROUP BY LENGTH(`column_a`) 
ORDER BY LENGTH(`column_a`) DESC LIMIT 1

就像在下一个查询中添加了两列(最大长度和行 ID)

SHOW COLUMNS FROM `table`

也许也使用这个查询来获取特定表的列名:

SELECT `column_name` AS `column` 
FROM `information_schema`.`columns` 
WHERE `table_name` = 'table' 
AND `column_name` != 'id' 
ORDER BY `ordinal_position`

差不多了(感谢 Bill)...(只需要指定 'table')但是现在如何运行 _SQL al 一样的运行...

SELECT CONCAT(GROUP_CONCAT(CONCAT('(SELECT \'', `column_name`,'\' AS `column`, LENGTH(`', `column_name`,'`) AS `length`, id ', 'FROM `', `table_schema`,'`.`', `table_name`,'` ORDER BY `length` DESC LIMIT 1)') SEPARATOR ' UNION ALL '), ';') AS _SQL
FROM `information_schema`.`columns` 
WHERE `table_name` = 'table' 
  AND `column_name` IN (
    SELECT `column_name`
    FROM `information_schema`.`columns` 
    WHERE `table_name` = 'table' 
    AND `column_name` != 'id' 
    ORDER BY `ordinal_position`);

【问题讨论】:

  • 架构看起来格式不正确。你能描述一下这个应用程序吗,或者这只是一个智力练习?当列名是数据时,这是一个不好的信号,不适合 RDBMS。
  • “智力练习”听起来不错,但它是用于监控值长度的。起初我以为这只是一个简单的查询,我无法做出/找到/提出。但现在这似乎是一项艰巨的任务。比尔此时给出了最好的解决方案,尽管我目前无法在同一运行中运行创建的查询。

标签: mysql max


【解决方案1】:

MySQL 没有任何方法可以在单个查询中执行您想要的操作。在您准备查询时,必须修复对列的所有引用。

您可以做的最好的事情是对 INFORMATION_SCHEMA 使用一个查询来生成一个新的动态 SQL 查询,该查询可以获取您想要的信息。

我在 MySQL 5.5.16 上对此进行了测试:

SELECT CONCAT(GROUP_CONCAT( 
  CONCAT('(SELECT \'',COLUMN_NAME,'\' AS `column`, LENGTH(`',COLUMN_NAME,'`) AS `length`, id ',
  'FROM `',TABLE_SCHEMA,'`.`',TABLE_NAME,'` ORDER BY `length` DESC LIMIT 1)')
  SEPARATOR ' UNION ALL '), ';') AS _SQL
FROM INFORMATION_SCHEMA.COLUMNS
WHERE (TABLE_SCHEMA, TABLE_NAME) = ('test', 'tt') 
  AND COLUMN_NAME IN ('a', 'b', 'c');

此查询产生以下输出(为了在此处发布,我添加了换行符):

(SELECT 'a' AS `column`, LENGTH(`a`) AS `length`, id 
 FROM `test`.`tt` ORDER BY `length` DESC LIMIT 1) 
UNION ALL
(SELECT 'b' AS `column`, LENGTH(`b`) AS `length`, id 
 FROM `test`.`tt` ORDER BY `length` DESC LIMIT 1)
UNION ALL
(SELECT 'c' AS `column`, LENGTH(`c`) AS `length`, id 
 FROM `test`.`tt` ORDER BY `length` DESC LIMIT 1);

生成的查询在我的测试中返回以下结果:

+--------+--------+----+
| column | length | id |
+--------+--------+----+
| a      |     10 |  2 | 
| b      |      9 |  1 | 
| c      |      6 |  2 | 
+--------+--------+----+

请注意,这并不能解决关系。如果多行具有相同的最长字符串,它将只报告一个id,任意选择。


你的额外问题:

您必须将这些作为两个单独的查询执行。我在回答的开头说过,在准备查询时,列引用必须在 SQL 字符串中固定。您无法动态发现列并在同一查询中查询它们的数据。


你的评论:

GROUP_CONCAT()返回的最长字符串默认是1024,只够生成这个9列的SQL查询。您可以提高此限制:

SET group_concat_max_len = 1024*1024;

这只会增加当前数据库会话的限制。您可以使用SET GLOBAL ... 为所有会话更改它。如果您希望在重启 MySQL 后保持这种状态,请在 my.cnf 文件中设置该值(无需在配置文件中使用 SET GLOBAL)。

【讨论】:

  • 谢谢,快到了...在我的问题底部查看我的补充。
  • 来自比尔的更多信息:mysqlperformanceblog.com/2011/11/18/…
  • 我构建的查询长于 _SQL,有没有办法先制作 _SQL 类型的文本?现在它只是减少了查询的生成。
【解决方案2】:

解决这个问题会有点棘手,所以我不打算写确切的 SQL,但大致思路如下:

  1. 创建一个表变量来保存您的结果。它将与您在答案中发布的架构相同。

  2. 创建另一个表变量来保存您想要透视的所有列名。您只需要一个带有名称列的表。

  3. 将列名插入到第 2 步创建的临时表中:

    插入@TempTable (ColumnName) 从 information_schema.columns 中选择 COLUMN_NAME,其中 Table_Name = 'YourTable';

  4. 使用游标,遍历列名表变量中的每一列。

  5. 在循环中,您将使用 PREPARE STATEMENT 函数创建并执行动态 SQL 查询。循环中的每次迭代都会在结果表中插入一行以获取单个列名。

    从@query 准备 STMT; 执行STMT;

这里是对需要执行的动态 SQL 的尝试。 @columnName 是循环中变量的名称。

declare @maxLength int;
select @maxLength =  max(length(@ColumnName)) maxlength from YourTable;
insert into @results (ColumnName, MaxLength, RowId)
select '@ColumnName', @maxLength, Id from YourTable where length(@ColumnName)) = @maxLength;

或者,您可以将数据转储到 Excel 中并使用数据透视表功能:)

【讨论】:

    【解决方案3】:

    试试这个:

    SELECT
        length(concat('',a)) as maxlen,
        group_concat(if (length(concat('',a))=@maxlen_a,a,null)) as maxlenval
    FROM
        (select @maxlen_a:=max(length(concat('',a))) from tablename) as aview,
        tablename
    GROUP BY maxlen 
    HAVING maxlenval IS NOT null
    
    UNION
    
    SELECT
        length(concat('',b)) as maxlen,
        group_concat(if (length(concat('',b))=@maxlen_b,b,null)) as maxlenval
    FROM
        (select @maxlen_b:=max(length(concat('',b))) from tablename) as bview,
        tablename
    GROUP BY maxlen 
    HAVING maxlenval IS NOT null
    

    ... 每列以此类推。

    这样也解决了两个不同值长度相同的问题

    【讨论】:

    • 重要的是,MySQL 中没有反射之类的东西——从(描述表名)中选择字段作为反射;不会工作。
    • 我不知道这个查询到底是做什么的,但它不是动态的,我在每个表中有 50-100 列并且进行这样的查询(对于每列一个查询部分)不是真的是一个选择。对不起...
    • 正如我在第一条评论中指出的那样,这对于 MySQL 来说是不可能的:“从(描述表名)中选择字段”和“从(显示表名中的字段)中选择字段”都不起作用,所以在本质上,您不能将字段名视为数据,因此您不能在同一查询中查询跨字段名和字段值。
    • 离题:为什么不使用其他工具来自动创建查询?为什么不使用这个外部工具不仅自动创建查询,还包装存储过程?
    • 我有使用这个查询的所有列名:SELECT column_name AS column FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'table' ORDER BY ordinal_position
    【解决方案4】:

    如果我理解你的需要,这样的东西应该可以工作(在 Mysql 5.1 上测试):

    SELECT  
        column, length, id 
    FROM 
        (
         (SELECT 'a' AS column, CHAR_LENGTH(a) AS length, id 
          FROM yourtable 
          ORDER BY length DESC 
          LIMIT 1)
         UNION
         (SELECT 'b' AS column, CHAR_LENGTH(b) AS length, id 
          FROM yourtable 
          ORDER BY length DESC 
          LIMIT 1)
         UNION
         (SELECT 'c' AS column, CHAR_LENGTH(c) AS length, id 
          FROM yourtable 
          ORDER BY length DESC 
          LIMIT 1)
         UNION 
         (...)
    ) T
    

    【讨论】:

    • 这不是我想要的。更像是使用 (SHOW COLUMNS FROM table) 添加两列,每一列的最大长度为 MAX(LENGTH()) 和行 ID。我有多个表,每个表 50-100 列。所以它必须是动态的。
    猜你喜欢
    • 1970-01-01
    • 2013-07-17
    • 1970-01-01
    • 2011-06-23
    • 1970-01-01
    • 2013-05-19
    • 1970-01-01
    • 1970-01-01
    • 2017-04-04
    相关资源
    最近更新 更多