对这两天MySQL的一些总结

本文探讨了在Java开发中使用MySQL进行多表关联查询的方法,特别是如何处理复杂的多表左连接(left join)问题,并提供了批量更新数据的有效解决方案。

回归开始做Java,因为以前都是一些小项目,而且表结构都不是很复杂,所以也没什么好说的,但是现在开始专职做Java之后,现在MySQL还只是一个新手,开一个博客专门整理自己的一些问题,这样以后查看自己的blog就可以了。

1.关于left join

因为要做多表关联,但是在网上找的demo都太简单了,只能自己用命令一点一点试验。。过程很纠结。。现在有一个表和四个表的主键有关联,就根据以前同事写的一些代码,和网上的一些讲解写了一个

SELECT
	a.*, b.create_time,
	b.frequency,
	c.sourceDb as sourceDb,
	d.destDb as destDb,
	e.sourceTable as sourceTable,
	f.destTable as destTable,
	g.sourceField as sourceField,
	h.destField as destField
FROM
	a
RIGHT JOIN(
	SELECT
		um.frequency,
		um.create_time,
		um.model_id
	FROM
		um
	WHERE
		um.is_display = 1
	AND um.user_id = #{user_id}
)b ON b.model_id = a.model_id
LEFT JOIN(
	SELECT
		db.db_name AS sourceDb,
		db.db_id
	FROM
		db
)c ON c.db_id = a.source_db_id
LEFT JOIN(
	SELECT
		db.db_name AS destDb,
		db.db_id
	FROM
		db
)d ON d.db_id = a.dest_db_id
LEFT JOIN(
	SELECT
		db.table_name AS sourceTable,
		db.table_id
	FROM
		db
)e ON e.table_id = a.source_table_id
LEFT JOIN(
	SELECT
		db.table_name AS destTable,
		db.table_id
	FROM
		db
)f ON f.table_id = a.dest_table_id
LEFT JOIN(
	SELECT
		df.col_name AS sourceField,
		df.col_id
	FROM
		df
)g ON g.col_id = a.source_field_id
LEFT JOIN(
	SELECT
		df.col_name AS destField,
		df.col_id
	FROM
		df
)h ON h.col_id = a.dest_field_id

其中B表是管理A表的。。B表是用户-模型表,A表是模型表,其他的表都是关联的A表的一些字段。但是查询出来之后发现,如果B表存在脏数据,那么right join就会出现问题,就会把脏数据也显示出来。。因为right join就是说以B表的数据条目为依据查询。就很纠结,所以准备放飞思想,按照自己理解的方式写一个SQL,因为本人没有钻研过SQL,所以确实是根据经验来推测的。。。下面是修改的SQL

SELECT
	a.*, b.create_time,
	b.frequency,
	c.sourceDb,
	d.destDb,
	e.sourceTable,
	f.destTable,
	g.sourceField,
	h.destField
FROM
	 b,
	a
LEFT JOIN(
	SELECT
		db.db_name AS sourceDb,
		db.db_id
	FROM
		 db
)c ON c.db_id = a.source_db_id
LEFT JOIN(
	SELECT
		db.db_name AS destDb,
		db.db_id
	FROM
		 db
)d ON d.db_id = a.dest_db_id
LEFT JOIN(
	SELECT
		db.table_name AS sourceTable,
		db.table_id
	FROM
		 db
)e ON e.table_id = a.source_table_id
LEFT JOIN(
	SELECT
		db.table_name AS destTable,
		db.table_id
	FROM
		 db
)f ON f.table_id = a.dest_table_id
LEFT JOIN(
	SELECT
		df.col_name AS sourceField,
		df.col_id
	FROM
		 df
)g ON g.col_id = a.source_field_id
LEFT JOIN(
	SELECT
		df.col_name AS destField,
		df.col_id
	FROM
		 df
)h ON h.col_id = a.dest_field_id
WHERE
	b.is_display = 1
AND b.user_id = #{user_id}
AND a.model_id = b.model_id

这样写发现可以执行,,我就认真分析了一下,这样是先查询出来A,B然后根据查询出来的数据去关联left join 。。

2 批量更新

说道批量更新就很头疼。。因为查了很多天,,在一个文章里面竟然还有说MySQL里面可以在MySQL里面直接执行foreach update语句。。。例如

<foreach collection="list" index="index" item="item" separator=";">
update df set comment =#{item.comment}
</foreach>

这样写会报错的。。大概意思是不支持。

后台就用case when ,大概就是这样

         update df
        <trim prefix="set" suffixOverrides=",">
            <trim prefix="source_col_type =case" suffix="end,">
                <foreach collection="list" item="item" index="index">
                    when id=#{id} then #{item.sourceColType}
                </foreach>
            </trim>
        </trim> 
        where id in 
        <foreach collection="list" index="index" item="item" separator="," open="(" close=")">
            #{item.id}
        </foreach>

解释一下,prefix的意思是说在SQL语句之前拼接这个字符,suffixOverrides的意思是去掉最后一个重复的这个字符,上面的这个在MySQL执行的语句是这样的

UPDATE df
SET source_col_type = CASE
WHEN id = '1' THEN
	'a'
WHEN id = '2' THEN
	'b'
WHEN id = '3' THEN
	'c'
END
WHERE
	id IN(1, 2, 3)
case when类似于switch
switch(id){
  case 1:
  set 'a'
  break;
case 2:
  set 'b'
  break;
case 3:
  set 'c'
  break;
}

大概就是这个意思,,但是呢实际项目中我是没办法拿到id的。。。只能根据两个条件进行了。。所以要对上面的进行变形。。

update df
        <trim prefix="set" suffixOverrides=",">
            <trim prefix="source_col_type =case" suffix="end,">
                <foreach collection="list" item="item" index="index">
                    when name=#{name} and source=#{source} then #{item.sourceColType}
                </foreach>
            </trim>
        </trim> 
        where name in 
        <foreach collection="list" index="index" item="item" separator="," open="(" close=")">
            #{item.name}
        </foreach> 
        and source in 
      <foreach collection="list" index="index" item="item" separator="," open="(" close=")">
          #{item.source}
      </foreach>

如果你有多个判定条件就这样写。


注意,在SQL语句中,如果有SQL的关键字。。一定要用``这个包裹起来。。要不然报错了就。。。切记




<think>我们被要求查询最近两天有数据的数据库(注意是多个数据库),但MySQL中每个数据库是独立的,因此需要遍历所有数据库并检查每个数据库中是否有表在最近两天有数据。思路:1.获取MySQL服务器上所有数据库的列表(排除系统数据库如information_schema,mysql,performance_schema等)。2.对每个数据库,检查是否存在至少张表,在该表中有最近两天(即今天和昨天)的数据。这里“有数据”指的是在某个日期字段(比如trade_date,create_time等)的值在最近两天内。但是,由于每个数据库的表结构不同,我们可能不知道具体的表名和日期字段。因此,我们需要动态地检查每个数据库的每张表,看是否有符合条件的记录。然而,这个操作非常复杂且耗时,尤其是当数据库和表很多时。而且,在MySQL中没有直接的方法可以跨数据库执行这样的查询(因为每个数据库是独立的)。考虑到实际可行性,通常我们不会在单个查询中完成这样的任务,而是通过编存储过程或脚本(如Shell脚本、Python脚本等)来实现。下面,我将提供两种方法:方法:使用存储过程(适用于MySQL服务器端)方法二:使用脚本(例如Shell脚本)来遍历数据库和表由于问题要求查询的是“数据库”,而不是某个数据库中的表,所以我们需要检查每个数据库。但是,请注意,如果我们的目的是监控哪些数据库在最近两天有数据更新(插入/更新),那么我们需要每个表中都有一个时间戳字段(如update_time)来记录。而实际情况是,每个表的设计不同,因此这种方法并不通用。考虑到引用[1]中查询了特定表的最后天数据,我们可以假设每个数据库中我们只关心特定的表(比如交易记录表)的特定字段(如trade_date)。但问题要求的是“有数据的数据库”,所以我们可能需要检查所有表?这显然效率太低。因此,我们需要明确:是检查所有表还是只检查特定的表?由于问题中没有指定表,我们假设是要检查每个数据库中是否存在任意表在最近两天有数据。下面给出一个折中的方案:我们只检查每个数据库中的每张表,看它们是否有最近两天的数据(通过检查表中的时间戳字段,如果有的话)。但是,每个表的时间戳字段名可能不同(例如create_time,update_time,timestamp,trade_date等),因此我们需要预设些常见的字段名,或者要求表中有特定的字段名(这在实际中不太现实)。因此,更实际的做法是:我们只关注那些我们已知的、可能有时间戳字段的表(比如交易记录表、日志表等)。或者,我们可以要求在每个数据库中都有一个固定的表(例如data_status)来记录每天的数据情况,但这需要提前设计。鉴于问题的开放性,我提供一个基于存储过程的方法,该方法将:1.遍历所有非系统数据库。2.对于每个数据库,遍历所有表。3.对于每个表,检查是否存在以下类型的字段:DATETIME,TIMESTAMP,DATE(因为日期类型字段可以记录时间)。4.如果存在这样的字段,则检查该表中是否有最近两天的数据(即字段值大于等于前天零点,小于今天结束)。5.如果找到至少条记录,就记录该数据库并跳出当前数据库的循环(因为我们只关心数据库是否有数据,而不是具体哪张表)。但是,这种方法非常耗时,尤其对于大表。因此,我们可以限制检查范围(比如只检查每张表的前几条记录中是否有最近两天的数据?但这样可能不准确)。另外,如果表没有日期类型的字段,我们就跳过。考虑到性能,这个存储过程可能会对服务器造成较大压力,所以建议在服务器空闲时执行。由于编这样的存储过程比较复杂,这里我给出一个简化的版本,它假设每个数据库中至少有一个表有一个名为'create_time'的日期字段(或者我们可以指定多个可能的字段名)。以下是存储过程的步骤:1.创建一个临时表来存储结果(数据库名)。2.获取所有非系统数据库列表。3.循环数据库:4.切换当前数据库。5.获取该数据库所有用户表。6.循环表:7.检查该表中是否有日期类型的字段(这里假设我们使用字段名'create_time',或者我们可以定义一个列表,但MySQL存储过程不支持数组,所以我们可以使用临时表存储可能的字段名,然后在循环中检查是否在该表存在的列名中)。8.如果该表有日期类型字段(例如字段名为'create_time'),则构建一个动态SQL查询,检查该表是否有最近两天的数据。9.如果该表有,则将当前数据库名插入结果表,并跳出内层循环(继续下一个数据库)。10.返回结果。但是,由于MySQL存储过程不支持直接使用数组,我们需要提前定义一个可能的日期字段列表。我们可以创建一个临时表来存储这些可能的字段名(比如'create_time','update_time','trade_date'等),然后在循环中匹配。由于这个过程非常复杂且对性能影响大,通常在实际中并不这样做。因此,我们可能需要考虑其他方法,比如在应用层记录数据库的更新情况。鉴于上述复杂性,我建议采用另种思路:在数据插入时记录到张系统表中(记录数据库名、表名、最后更新时间)。然后查询这个系统表即可。但是,用户的问题是要直接通过查询来实现。所以这里提供一个简化版的存储过程(仅作为思路参考,未经过全面测试,请谨慎使用):步骤:1.创建张临时表来存储结果(db_namevarchar(255))。2.获取非系统数据库列表(db_name)存入游标。3.循环数据库游标:4.SET@db=当前数据库名5.获取该数据库下所有表的列表(使用SELECTtable_nameFROMinformation_schema.tablesWHEREtable_schema=@db)存入游标。6.循环表游标:7.SET@table=当前表名8.检查该表是否有我们预设的日期字段(例如'create_time'):使用信息模式(information_schema.columns)检查该表是否存在列名为'create_time'且数据类型为日期类型的列。9.如果有,则构建动态SQL:`SET@sql=CONCAT('SELECT1FROM`',@db,'`.`',@table,'`WHERE`create_time`>=CURRENT_DATE()-INTERVAL1DAYAND`create_time`< CURRENT_DATE()+INTERVAL1DAYLIMIT1');`(注意:这里我们查询的是最近两天,即从昨天零点到今天结束)也可以调整为:`WHEREcreate_time>=DATE_SUB(CURDATE(),INTERVAL1DAY)ANDcreate_time< DATE_ADD(CURDATE(),INTERVAL1DAY)`10.准备和执行该SQL,如果有结果(查询到至少行),则将当前数据库名插入临时结果表,并跳出内层循环(表循环)。11.关闭表游标(内层循环)。12.结束数据库循环,返回结果。但是,由于需要嵌套游标,且动态SQL在存储过程中处理复杂,这里我们提供一个简化的伪代码(实际编需要处理很多异常和游标释放)。另外,注意日期字段可能有不同的名字,我们可以用多个OR条件,但这里只用一个字段名示例。考虑到实际执行的时间和资源消耗,这个方法不适用于大量数据库或大表。因此,我们仅提供一个参考思路。另种简单思路:如果我们知道每个数据库中都有张特定的表(比如叫`t_data_status`),并且这张表有一个日期字段(`last_updated`),那么查询就会简单很多:我们可以查询所有数据库中`t_data_status`表的`last_updated`在最近两天的记录,然后返回数据库名。但是,如果没有这样的设计,我们就需要按照上面的复杂方法。鉴于问题的实际约束,我推荐使用脚本方法(如Shell脚本)来遍历数据库,然后对每个数据库运行一个查询(查询预设的表和日期字段)。如果预设表不存在,或者日期字段不存在,则跳过。以下是一个Shell脚本的示例:```bash#!/bin/bash#MySQL连接配置MYSQL_USER="username"MYSQL_PASS="password"#获取所有数据库列表,排除系统数据库DATABASES=$(mysql-u$MYSQL_USER-p$MYSQL_PASS-e"SHOWDATABASES;"|grep-Ev"Database|information_schema|mysql|performance_schema|sys")#最近两天的时间范围:从昨天零点到今天结束START_TIME=$(date-d"1dayago00:00:00""+%Y-%m-%d%H:%M:%S")END_TIME=$(date-d"today23:59:59""+%Y-%m-%d%H:%M:%S")#或者我们只按日期(忽略时间部分),那么可以简化:最近两天包括今天和昨天#我们可以使用CURDATE()-INTERVAL1DAY和CURDATE()+INTERVAL1DAY#遍历每个数据库forDBin$DATABASES;do#检查该数据库中是否有表在最近两天有数据#假设我们只检查一个表't_data_status',并有一个字段'last_updated'QUERY="SELECTCOUNT(*)FROMt_data_statusWHERElast_updatedBETWEEN'$START_TIME'AND'$END_TIME'"COUNT=$(mysql-u$MYSQL_USER-p$MYSQL_PASS-D$DB-e"$QUERY"-s-N)if["$COUNT"-gt0];thenecho"Database$DBhasdatainthelasttwodays."fidone```注意:这个脚本假设每个数据库都有一个表`t_data_status`和一个字段`last_updated`。如果没有,则会报错,我们可以忽略错误(将错误输出重定向到/dev/null)或者检查表是否存在。如果我们不确定表名和字段名,我们可以修改脚本,尝试多个表名和字段名。但这样脚本就会变得复杂。综上,我们提供两种方案:方案1(推荐):在数据库设计时,每个数据库都设置一个状态表(如`data_status`),记录最后次数据更新的时间。这样查询就很简单。方案2:使用存储过程或脚本动态检查,但是复杂且性能低。由于用户的问题没有明确表结构,我按照最简单的情况给出查询(假设每个数据库都有状态表`data_status`,且其中有一个字段`last_updated`):查询单个数据库的状态(最近两天):```sqlSELECTdatabase_nameFROMdata_statusWHERElast_updated>=CURRENT_DATE()-INTERVAL1DAY;```但是,这个状态表通常只存在一个数据库中(比如监控数据库),而不是每个数据库都有。所以我们需要在每个数据库中创建这样的表。所以,如果用户没有这样的表结构,那么就需要考虑使用复杂的存储过程或脚本(如上面的Shell脚本)并预设表名和字段名。因此,针对用户的问题,我建议先询问是否有统的状态记录表。如果没有,则需要采用动态检查的方法,但要谨慎使用。由于用户要求的是MySQL查询,而跨数据库的查询在MySQL中不能直接在一个SELECT中完成,所以我们无法用单个查询语句完成。所以,总结回答如下:由于MySQL中每个数据库是独立的,要查询最近两天有数据的数据库,般需要分别检查每个数据库。没有内置的SQL语句可以直接完成。建议采取以下两种方法之:1.如果每个数据库都有一个用于记录数据时间的状态表(例如名为`data_status`,包含时间字段`last_updated`),则可以使用一个脚本依次检查每个数据库的这张表。2.使用存储过程动态检查每个数据库的每张表(如果表中存在日期字段),但这种方法性能开销大。下面提供一个基于存储过程的简化示例(假设只有一个可能的日期字段名'create_time'):注意:这是一个示例,并未经过充分测试,使用前请备份数据并在测试环境验证。存储过程代码:```sqlDELIMITER//CREATEPROCEDUREFindRecentDatabases()BEGINDECLAREdoneINTDEFAULTFALSE;DECLAREdb_nameVARCHAR(255);DECLAREtable_nameVARCHAR(255);DECLAREdb_cursorCURSORFORSELECTschema_nameFROMinformation_schema.schemataWHEREschema_nameNOTIN('information_schema','mysql','performance_schema','sys');--临时表存储结果CREATETEMPORARYTABLEIFNOTEXISTSrecent_dbs(dbVARCHAR(255));OPENdb_cursor;db_loop:LOOPFETCHdb_cursorINTOdb_name;IFdoneTHENLEAVEdb_loop;ENDIF;--为表游标准备(获取当前数据库的所有表)SET@table_sql=CONCAT('SELECTtable_nameFROMinformation_schema.tablesWHEREtable_schema=\'',db_name,'\'');--这里需要预处理语句来获取表游标--由于存储过程不支持嵌套游标时直接打开动态SQL的游标,这里使用另种方法:遍历预定义的可能日期字段名(本例简化:只用一个字段名'create_time'),并检查每个表是否有该字段--内层循环:检查每张表--我们需要另一个游标来循环表BEGINDECLAREdone_tablesINTDEFAULTFALSE;DECLAREtable_cursorCURSORFORSELECTtable_nameFROMinformation_schema.tablesWHEREtable_schema=db_name;DECLARECONTINUEHANDLERFORNOTFOUNDSETdone_tables=TRUE;OPENtable_cursor;table_loop:LOOPFETCHtable_cursorINTOtable_name;IFdone_tablesTHENLEAVEtable_loop;ENDIF;--检查该表是否有'create_time'字段且为日期类型SET@column_exists=0;SELECTCOUNT(*)INTO@column_existsFROMinformation_schema.columnsWHEREtable_schema=db_nameANDtable_name=table_nameANDcolumn_name='create_time'ANDdata_typeIN('datetime','timestamp','date');IF@column_exists>0THEN--构建查询语句SET@query=CONCAT('SELECT1FROM`',db_name,'`.`',table_name,'`WHEREcreate_time>=CURDATE()-INTERVAL1DAYANDcreate_time< CURDATE()+INTERVAL1DAYLIMIT1');--使用预处理语句执行查询PREPAREstmtFROM@query;EXECUTEstmt;IFFOUND_ROWS()>0THEN--有数据,记录数据库名INSERTINTOrecent_dbsVALUES(db_name);--注意:这里我们跳出内层循环,继续下一个数据库CLOSEtable_cursor;LEAVEtable_loop;ENDIF;DEALLOCATEPREPAREstmt;ENDIF;ENDLOOPtable_loop;CLOSEtable_cursor;END;ENDLOOP;CLOSEdb_cursor;--输出结果SELECTdbAS'Databaseswithrecentdata'FROMrecent_dbs;DROPTEMPORARYTABLErecent_dbs;END//DELIMITER;```然后调用:```sqlCALLFindRecentDatabases();```重要提醒:这个存储过程在数据库和表很多时可能会非常慢,并且会扫描表(即使有索引,因为我们使用的是范围查询,如果有索引则能加速)。另外,注意该存储过程会跳过没有'create_time'字段的表。如果有多个可能的字段名(如'update_time'、'trade_date'等),则需要在循环中增加检查。所以,综合来看,最可行的方法还是维护一个状态表或者使用日志记录数据更新时间。因此,我建议用户根据实际情况选择方案。如果只是临时检查,且数据量不大,可以使用存储过程。对于长期需求,建议设计状态记录表。由于问题复杂,以上提供的方法仅供参考,请根据实际环境调整。相关问题:1.如何监控MySQL数据库中表的更新情况?2.在MySQL中如何遍历所有数据库中的所有表?3.如何编MySQL存储过程来处理动态SQL?如果用户需要,可以进步讨论这些问题。</think> ### 如何查询最近两天有数据的MySQL数据库 在MySQL中,查询最近两天有数据的数据库需要分步骤操作。以下是具体实现方法(假设数据库中存在时间字段 `trade_date` 或类似字段): #### 步骤1:获取所有数据库列表 ```sql SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ( 'information_schema', 'mysql', 'performance_schema', 'sys' ); ``` #### 步骤2:动态生成数据库检查语句 ```sql SET GROUP_CONCAT_MAX_LEN = 10000; SELECT GROUP_CONCAT( CONCAT( 'SELECT \'', schema_name, '\' AS db, ', 'MAX(trade_date) AS last_date ', 'FROM ', schema_name, '.table_name ', 'HAVING last_date >= CURDATE() - INTERVAL 1 DAY' ) SEPARATOR ' UNION ALL ' ) FROM information_schema.schemata WHERE schema_name NOT IN ('information_schema', 'mysql'); ``` #### 步骤3:执行生成的查询(示例输出) ```sql SELECT 'db_finance' AS db, MAX(trade_date) AS last_date FROM db_finance.stock_data HAVING last_date >= CURDATE() - INTERVAL 1 DAY UNION ALL SELECT 'db_sales' AS db, MAX(order_time) AS last_date FROM db_sales.orders HAVING last_date >= CURDATE() - INTERVAL 1 DAY; ``` #### 关键要点 1. **时间字段适配** - 替换 `trade_date` 为实际时间字段名(如 `create_time`, `update_time`) - 使用 `MAX(field)` 获取最新数据时间[^1][^3] 2. **数据库过滤** - 排除系统数据库 (`information_schema` 等)[^4] - 根据实际业务需求调整数据库列表 3. **表名指定** - 替换 `table_name` 为需要监控的核心表名 - 多表监控需用 `UNION ALL` 连接查询 > ⚠️ 注意:此方法需要用户对库表结构有基础了解。若需全自动检测,建议创建存储过程动态遍历所有表的时间字段。 #### 示例结果 | 数据库名 | 最后数据日期 | |------------|----------------| | db_finance | 2023-06-14 | | db_logs | 2023-06-15 | --- ### 相关问题 1. 如何监控MySQL数据库每日数据增量?[^4] 2. 怎样高效查询MySQL大时间范围的数据(如5亿条记录)?[^3] 3. MySQL多表关联查询时如何进行性能优化?[^2] 4. 如何自动检测MySQL中长时间无更新的数据库?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值