在MySQL表中查找所有只有空值的列

1wnzp6jl  于 2022-12-17  发布在  Mysql
关注(0)|答案(7)|浏览(197)

情况如下:
我有大量的表,每个表都有大量的列,我需要在新系统中处理这个旧的、即将废弃的数据库,我正在寻找一种方法来消除所有显然从未使用过的列。
我想通过过滤掉在任何给定行上具有值的所有列来实现这一点,留下一组所有行中的值都为NULL的列。当然,我可以手动对每一列进行降序排序,但这会花费太长时间,因为我需要处理大量的表和列。我估计这将需要400个表,每个表最多50(!)列。
有什么方法可以从information_schema中获得这些信息吗?

编辑:

下面是一个例子:

column_a    column_b    column_c    column_d
NULL        NULL        NULL        1
NULL        1           NULL        1
NULL        1           NULL        NULL
NULL        NULL        NULL        NULL

输出应为“column_a”和“column_c”,因为这是仅有的没有任何填充值的列。

tct7dpnv

tct7dpnv1#

通过动态创建(从INFORMATION_SCHEMA.COLUMNS表)一个包含要执行的SQL的字符串,然后从该字符串创建preparing a statement并执行它,可以避免使用过程。
我们希望构建的SQL如下所示:

SELECT * FROM (
  SELECT 'tableA' AS `table`,
         IF(COUNT(`column_a`), NULL, 'column_a') AS `column`
  FROM   tableA
UNION ALL
  SELECT 'tableB' AS `table`,
         IF(COUNT(`column_b`), NULL, 'column_b') AS `column`
  FROM   tableB
UNION ALL
  -- etc.
) t WHERE `column` IS NOT NULL

这可以通过以下方式实现:

SET group_concat_max_len = 4294967295; -- to overcome default 1KB limitation

SELECT CONCAT(
         'SELECT * FROM ('
       ,  GROUP_CONCAT(
            'SELECT ', QUOTE(TABLE_NAME), ' AS `table`,'
          , 'IF('
          ,   'COUNT(`', REPLACE(COLUMN_NAME, '`', '``'), '`),'
          ,   'NULL,'
          ,    QUOTE(COLUMN_NAME)
          , ') AS `column` '
          , 'FROM `', REPLACE(TABLE_NAME, '`', '``'), '`'
          SEPARATOR ' UNION ALL '
         )
       , ') t WHERE `column` IS NOT NULL'
       )
INTO   @sql
FROM   INFORMATION_SCHEMA.COLUMNS
WHERE  TABLE_SCHEMA = DATABASE();

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

在SQLFIDLE上看吧。

cfh9epnr

cfh9epnr2#

我不是SQL过程方面的Maven,因此给出了使用SQL查询和PHP/Python脚本的一般概念。

  • INFORMATION_SCHEMA数据库上使用SHOW TABLES或其他查询来获取数据库MY_DATABASE中的所有表
  • 执行一个查询以生成一个语句来获取特定表中的所有列名,这将在下一个查询中使用。
SELECT Group_concat(Concat( "MAX(", column_name, ")" ))
         FROM   information_schema.columns
         WHERE  table_schema = 'MY_DATABSE'
                AND table_name = 'MY_TABLE'
         ORDER  BY table_name,ordinal_position
  • 您将获得类似MAX(column_a),MAX(column_b),MAX(column_c),MAX(column_d)的输出
  • 使用此输出生成最终查询:

从MY_DATABASE.MY_TABLE中选择最大值(a列)、最大值(B列)、最大值(c列)、最大值(d列)
输出结果为:

MAX(column_a)    MAX(column_b)   MAX(column_c)   MAX(column_d)
     NULL            1           NULL                1
  • 最大值为NULL的所有列都是值为NULL的列
kse8i1jr

kse8i1jr3#

您可以利用 COUNT 聚集函数关于NULL的行为。通过将字段作为参数传递,COUNT 函数将返回非NULL值的数量,而COUNT(*)将返回总行数。因此,您可以计算NULL与“可接受”值的比率。
我将给予一个具有以下表格结构的示例:

CREATE TABLE `t1` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
   `col_1` int(10) unsigned DEFAULT NULL,
   `col_2` int(10) unsigned DEFAULT NULL,
   PRIMARY KEY (`id`)
) ;

-- let's fill the table with random values
INSERT INTO t1(col_1,col_2) VALUES(1,2);
INSERT INTO t1(col_1,col_2) 
SELECT 
IF(RAND() > 0.5, NULL ,FLOOR(RAND()*1000), 
IF(RAND() > 0.5, NULL ,FLOOR(RAND()*1000) FROM t1;

-- run the last INSERT-SELECT statement a few times
SELECT COUNT(col_1)/COUNT(*) AS col_1_ratio, 
COUNT(col_2)/COUNT(*) AS col_2_ratio FROM t1;

您可以编写一个函数,通过将表名作为输入变量传递,自动从INFORMATION_SCHEMA数据库构造查询。以下是直接从INFORMATION_SCHEMA表获取结构数据的方法:

SET @query:=CONCAT("SELECT @column_list:=GROUP_CONCAT(col) FROM (
SELECT CONCAT('COUNT(',c.COLUMN_NAME,')/COUNT(*)') AS col
FROM INFORMATION_SCHEMA.COLUMNS c 
WHERE NOT COLUMN_KEY IN('PRI') AND TABLE_SCHEMA=DATABASE() 
AND TABLE_NAME='t1' ORDER BY ORDINAL_POSITION ) q");
PREPARE COLUMN_SELECT FROM @query;
EXECUTE COLUMN_SELECT;
SET @null_counters_sql := CONCAT('SELECT ',@column_list, ' FROM t1');
PREPARE NULL_COUNTERS FROM @null_counters_sql;
EXECUTE NULL_COUNTERS;
zbq4xfa0

zbq4xfa04#

SQL Fiddle演示链接

我已经创建了4个表,其中3个用于演示,另外一个nullcolumns是解决方案的强制部分,在这3个表中,只有salarydept的列的值全部为空(您可以看看它们的脚本)。
最后给出了强制性表格和程序
您可以在本地主机上的所需数据库中复制粘贴并运行(强制部分或全部)sql(只需将分隔符更改为//),然后---call get();,然后查看结果

CREATE TABLE IF NOT EXISTS `dept` (
  `did` int(11) NOT NULL,
  `dname` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`did`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `dept` (`did`, `dname`) VALUES
(1, NULL),
(2, NULL),
(3, NULL),
(4, NULL),
(5, NULL);

CREATE TABLE IF NOT EXISTS `emp` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ename` varchar(50) NOT NULL,
  `did` int(11) NOT NULL,
  PRIMARY KEY (`ename`),
  KEY `deptid` (`did`),
  KEY `id` (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;

INSERT INTO `emp` (`id`, `ename`, `did`) VALUES
(1, 'e1', 4),
(2, 'e2', 4),
(3, 'e3', 2),
(4, 'e4', 4),
(5, 'e5', 3);

CREATE TABLE IF NOT EXISTS `salary` (
  `EmpCode` varchar(50) NOT NULL,
  `Amount` int(11) DEFAULT NULL,
  `Date` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `salary` (`EmpCode`, `Amount`, `Date`) VALUES
('1', 344, NULL),
('2', NULL, NULL);

------------------------------------------------------------------------
------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS `nullcolumns` (
  `Table_Name` varchar(100) NOT NULL,
  `Column_Name` varchar(100) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--Only one procedure Now
CREATE PROCEDURE get(dn varchar(100))
BEGIN
declare c1 int; declare b1 int default 0; declare tn varchar(30);
declare c2 int; declare b2 int; declare cn varchar(30);

select count(*) into c1 from information_schema.tables where table_schema=dn;
delete from nullcolumns;
while b1<c1 do
select table_name into tn from information_schema.tables where
table_schema=dn limit b1,1;        

select count(*) into c2 from information_schema.columns where
table_schema=dn and table_name=tn;
set b2=0;
while b2<c2 do
select column_name into cn from information_schema.columns where
table_schema=dn and table_name=tn limit b2,1;

set @nor := 0;
set @query := concat("select count(*) into @nor from ", dn,".",tn);
prepare s1 from @query;
execute s1;deallocate prepare s1;

if @nor>0 then set @res := 0;
set @query := concat("select ((select max(",cn,") from ", dn,".",tn,")
is NULL) into @res");
prepare s1 from @query;
execute s1;deallocate prepare s1;

if @res=1 then
insert into nullcolumns values(tn,cn);
end if; end if;

set b2=b2+1;
end while;

set b1=b1+1;
end while;
select * from nullcolumns;
END;

您可以轻松地执行存储过程轻松地作为sql在您的phpmyadin 'as it is'只需更改分隔符(在底部的SQL查询框)为// Then

call get();

并且享受:)
现在,您可以看到表nullcolumns,其中显示了具有100/100空值的所有列沿着表Names
在程序代码if @nor>0限制结果中不应包含空表中,您可以删除该限制。

bkhjykvo

bkhjykvo5#

我认为您可以使用GROUP_CONCAT和GROUP BY来完成此操作:

select length(replace(GROUP_CONCAT(my_col), ',', ''))
from my_table
group by my_col

(* 未测试 *)

EDIT:文档似乎没有说明GROUP_CONCAT需要相应的GROUP BY,因此请尝试以下操作:

select 
    length(replace(GROUP_CONCAT(col_a), ',', '')) as len_a
    , length(replace(GROUP_CONCAT(col_b), ',', '')) as len_b
    , length(replace(GROUP_CONCAT(col_c), ',', '')) as Len_c
from my_table
u91tlkcl

u91tlkcl6#

您可以使用MySQL的信息方案提供的预准备语句来完成此操作:

SET @TABLE_NAME= '...';

SET SESSION group_concat_max_len = 1000000;

SELECT 
CONCAT('SELECT * FROM (', 
    Group_concat(CONCAT("SELECT '", COLUMN_NAME, "' AS n, MAX(", COLUMN_NAME, ") AS v FROM ", @TABLE_NAME ) SEPARATOR ' UNION ALL ')
    , ") x WHERE v IS NOT null"
)
INTO @q
FROM   information_schema.columns
WHERE  table_schema = (SELECT DATABASE())
AND table_name = @TABLE_NAME
ORDER  BY table_name,ordinal_position
;

         
PREPARE ps FROM @q;
EXECUTE ps;
lmyy7pcs

lmyy7pcs7#

select column_name
from user_tab_columns
where table_name='Table_name' and num_nulls>=1;

只需简单的查询,您将获得这两列。

相关问题