情况如下:
我有大量的表,每个表都有大量的列。我需要为新系统处理这个旧的和待弃用的数据库,我正在寻找一种方法来消除所有已经 - 显然 - 从未使用过的列。
我想通过过滤掉在任何给定行上具有值的所有列来做到这一点,留下一组列,其中所有行中的值为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',因为它是唯一没有任何填充值的列。
答案 0 :(得分:17)
您可以通过动态创建(来自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;
在sqlfiddle上查看。
答案 1 :(得分:10)
我不是SQL程序方面的专家,因此使用SQL查询和PHP / python脚本给出了一般的想法。
使用SHOW TABLES
或INFORMATION_SCHEMA
数据库上的其他查询来获取数据库中的所有表格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)
使用此输出生成最终查询:
SELECT Max(column_a),Max(column_b),Max(column_c),Max(column_d)FROM MY_DATABASE.MY_TABLE
输出结果为:
MAX(column_a) MAX(column_b) MAX(column_c) MAX(column_d)
NULL 1 NULL 1
NULL
的所有列都包含所有值NULL
答案 2 :(得分:4)
您可以利用 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;
答案 3 :(得分:4)
我创建了4个表。三个用于演示,一个用于nullcolumns
是解决方案的必修部分。在三个表中,只有salary
和dept
具有所有值为null的列(您可以查看其脚本)。
最后给出了必修表和程序
您可以将粘贴并运行(必须部分或全部)作为sql(只需将分隔符更改为//)复制到localhost上的所需数据库中,然后--- 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;
您可以轻松地在phpmyadin中以sql的形式轻松执行存储过程,因为它只是将分隔符(在SQL问题框的底部)更改为//然后
call get();
享受:)
您现在可以看到表nullcolumns
显示所有具有100/100空值的列以及表Names
在过程代码中if @nor>0
限制在结果中不应包含空表,您可以删除该限制。
答案 4 :(得分:0)
我认为您可以使用GROUP_CONCAT和GROUP BY执行此操作:
select length(replace(GROUP_CONCAT(my_col), ',', ''))
from my_table
group by my_col
(未测试)
编辑:文档似乎没有声明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
答案 5 :(得分:-2)
select column_name
from user_tab_columns
where table_name='Table_name' and num_nulls>=1;
只需通过简单的查询,您就可以获得这两列。