通过游标遍历数据库中的用户表,找出所有含有NULL值字段的表名和字段名

问题贴:http://topic.youkuaiyun.com/u/20100422/17/af3b725c-d647-4d34-854b-4b6e8b056fae.html?15171

 

编写SQL脚本,可以遍历Test数据库的所有数据表,针对各表的所有数据记录,分析出内容为NULL的字段
--创建测试数据
use test
go
if object_id ( ' test.dbo.tb1 ' ) is not null drop table tb1
-- 创建数据表
create table tb1
(
a1
char ( 2 ),
b1
char ( 2 ),
c1
int
)
go
-- 插入测试数据
insert into tb1 select ' A ' , ' B ' , 2
union all select ' A ' , ' C ' , 3
union all select ' A ' , ' D ' , null
union all select ' B ' , null , 3
union all select ' B ' , ' E ' , 8
union all select ' E ' , ' F ' , null
union all select ' E ' , ' G ' , 3
go
if object_id ( ' test.dbo.tb2 ' ) is not null drop table tb2
-- 创建数据表
create table tb2
(
a2
char ( 2 ),
b2
char ( 2 ),
c2
int
)
go
-- 插入测试数据
insert into tb2 select ' A ' , ' B ' , 2
union all select ' A ' , ' C ' , null
union all select ' A ' , ' D ' , 5
union all select ' B ' , ' C ' , 3
union all select null , ' E ' , null
union all select ' E ' , ' F ' , 2
union all select ' E ' , ' G ' , 3
go
if object_id ( ' test.dbo.tb3 ' ) is not null drop table tb3
-- 创建数据表
create table tb3
(
a3
char ( 2 ),
b3
char ( 2 ),
c3
int
)
go
-- 插入测试数据
insert into tb3 select ' A ' , ' B ' , 2
union all select ' A ' , ' C ' , 3
union all select ' A ' , ' D ' , 5
union all select ' B ' , ' C ' , 3
union all select ' B ' , ' E ' , 8
union all select ' E ' , ' F ' , 2
union all select ' E ' , ' G ' , 3
go
--查询
--------------------------------------------------------------------------
--  Author : htl258(Tony)
--  Date   : 2010-04-22 20:28:29
--  Version:Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86)
--          Jul  9 2008 14:43:34
--          Copyright (c) 1988-2008 Microsoft Corporation
--          Developer Edition on Windows NT 5.1 <X86> (Build 2600: Service Pack 3)
--------------------------------------------------------------------------
IF OBJECT_ID('TEMPDB..#T') IS NOT NULL
   
DROP TABLE #T
GO
CREATE TABLE #T(TBNAME NVARCHAR(120),COLNAME NVARCHAR(120))
SET NOCOUNT ON
DECLARE @S NVARCHAR(4000)
DECLARE C CURSOR FOR
   
SELECT 'IF EXISTS(SELECT TOP 1 1 FROM ['+B.NAME+'] WHERE ['+A.NAME+'] IS NULL)
                INSERT #T SELECT
'''+B.NAME+''','''+A.NAME+''''
   
FROM SYSCOLUMNS A JOIN SYSOBJECTS B ON A.ID=B.ID AND B.TYPE='U'
OPEN C
FETCH C INTO @S
WHILE @@FETCH_STATUS=0
BEGIN
   
EXEC(@S)
   
FETCH C INTO @S
END
CLOSE C
DEALLOCATE C
SET NOCOUNT OFF
SELECT * FROM #T

 

/*
TBNAME               COLNAME
-------------------- --------------------
tb1                  b1
tb1                  c1
tb2                  a2
tb2                  c2

(4 行受影响)
*/


--另一种需求:找出整个字段都为NULL值的表名和列名。

use test

go

if object_id('test.dbo.tb1') is not null drop table tb1

-- 创建数据表

create table tb1

(

a1 char(2),

b1 char(2),

c1 int

)

go

--插入测试数据

insert into tb1 select 'A','B',2

union all select 'A','C',3

union all select 'A','D',null

union all select 'B',null,3

union all select 'B','E',8

union all select 'E','F',null

union all select 'E','G',3

go

if object_id('test.dbo.tb2') is not null drop table tb2

-- 创建数据表

create table tb2

(

a2 char(2),

b2 char(2),

c2 int

)

go

--插入测试数据

insert into tb2 select 'A','B',null

union all select 'A','C',null

union all select 'A','D',null

union all select 'B','C',null

union all select null,'E',null

union all select 'E','F',null

union all select 'E','G',null

go

if object_id('test.dbo.tb3') is not null drop table tb3

-- 创建数据表

create table tb3

(

a3 char(2),

b3 char(2),

c3 int

)

go

--插入测试数据

insert into tb3 select 'A','B',2

union all select 'A','C',3

union all select 'A','D',5

union all select 'B','C',3

union all select 'B','E',8

union all select 'E','F',2

union all select 'E','G',3

go

IF OBJECT_ID('TEMPDB..#T') IS NOT NULL

    DROP TABLE #T

GO

CREATE TABLE #T(TBNAME NVARCHAR(120),COLNAME NVARCHAR(120))

SET NOCOUNT ON

DECLARE @S NVARCHAR(4000)

DECLARE C CURSOR FOR

    SELECT 'IF NOT EXISTS(SELECT TOP 1 1 FROM ['+B.NAME+'] WHERE ['+A.NAME+'] IS NOT NULL)

                INSERT #T SELECT '''+B.NAME+''','''+A.NAME+''''

    FROM SYSCOLUMNS A JOIN SYSOBJECTS B ON A.ID=B.ID AND B.TYPE='U'

OPEN C

FETCH C INTO @S

WHILE @@FETCH_STATUS=0

BEGIN

    EXEC(@S)

    FETCH C INTO @S

END

CLOSE C

DEALLOCATE C

SET NOCOUNT OFF

SELECT * FROM #T

 

/*

TBNAME COLNAME

tb2     c2

*/

### 如何编写一个不依赖临时的 MySQL 存储过程 为了实现这一需求,可以通过以下方式构建存储过程: #### 1. 使用 `INFORMATION_SCHEMA.COLUMNS` 获取字段数据类型 MySQL 提供了一个元数据库 `INFORMATION_SCHEMA`,其中包含了关于数据库结构的信息。具体来说,`COLUMNS` 可以帮助我们查询某个特定中某列的数据类型。 #### 2. 遍历目标并检查每条记录 通过动态 SQL 游标的方式逐一读取目标中的每一行,并验证指定字段是否满足日期类型的条件。如果不满足,则将其写入日志。 以下是完整的存储过程代码示例: ```sql DELIMITER $$ CREATE PROCEDURE CheckAndLogNonDateRecords( IN tableName VARCHAR(255), IN columnName VARCHAR(255) ) BEGIN DECLARE done INT DEFAULT FALSE; DECLARE recordId BIGINT; -- 假设主键为BIGINT类型 DECLARE fieldValue DATE; -- 被检测的字段 DECLARE dataType VARCHAR(255); -- 字段的实际数据类型 -- 游标声明 DECLARE cur CURSOR FOR SELECT id, CAST(`columnName_` AS CHAR) FROM `tableName_`; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; -- 动态SQL获取字段实际数据类型 SET @dataTypeQuery = CONCAT('SELECT DATA_TYPE INTO @actualDataType FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''', tableName, ''' AND COLUMN_NAME = ''', columnName, ''''); PREPARE stmt FROM @dataTypeQuery; EXECUTE stmt; DEALLOCATE PREPARE stmt; IF @actualDataType != 'date' THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'The specified column is not of type date'; END IF; -- 打开游标前准备动态SQL查询语句 SET @curQuery = CONCAT('DECLARE cur CURSOR FOR SELECT id, ', columnName, ' FROM ', tableName); PREPARE stmtCur FROM @curQuery; EXECUTE stmtCur; DEALLOCATE PREPARE stmtCur; OPEN cur; read_loop: LOOP FETCH cur INTO recordId, fieldValue; IF done THEN LEAVE read_loop; END IF; -- 如果不是DATE类型则记录到日志 IF STR_TO_DATE(fieldValue, '%Y-%m-%d') IS NULL THEN INSERT INTO log_table (table_name, column_name, invalid_value, record_id) VALUES (tableName, columnName, fieldValue, recordId)[^1]; END IF; END LOOP; CLOSE cur; END$$ DELIMITER ; ``` 上述代码实现了如下功能: - **输入参数**:接受两个字符串参数——要检查的 (`tableName`) 字段名 (`columnName`)。 - **动态 SQL 查询**:利用 `INFORMATION_SCHEMA.COLUMNS` 来确认给定字段的数据类型是否为 `DATE` 类型。 - **游标的使用**:逐行扫描目标的内容,对于那些无法被解析成有效日期格式的,将它们插入到日志中。 #### 关于外键命约定 在外键定义方面,建议遵循标准命规则以保持一致性。例如,当创建指向其他的外键时,应采用类似于 `fk_childTable_parentTable` 这样的形式[^3]。 #### 日志记录注意事项 由于本方案未涉及任何显式的临时操作,因此无需担心二进制日志(binlog)配置问题[^4]。然而需要注意的是,在某些情况下,即使没有直接创建临时,隐含的动作仍可能触发 binlog 记录行为;这取决于具体的 MySQL 版本及其内部机制。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值