【问题标题】:Loading Multiple Tables from Multiple Schemas from MySQL to R, and Merge all the Tables从 MySQL 的多个模式中加载多个表到 R,并合并所有表
【发布时间】:2021-07-12 01:49:02
【问题描述】:

在开始之前,我知道存在以下问题: Get an R dataframe with merged values from multiple MySQL tablesBest approach to combine multiple MySQL tables in R。但我不能用它们来解决我的问题。

我在 MySQL 数据库中有一个模式列表(比如说 80 个模式),具有特定的 ProductID,例如 shj637、jsfh748、hasd548 等。我添加了一个具有 的特定表>特定名称在所有这些模式中作为 ProductINFO (所以所有模式都有这个表)。其中它们都具有相同的列名称(如 ID、名称、产品组等),但行中的值不同。

我想做的是创建一个循环或一个函数,它将加载union来自不同ProductINFO表>ProductID 架构,并使用所有包含其特定 ProductID 的信息(作为一列)创建一个数据框。

例如,如果:架构 A 带有 ProductID shj637 架构 B 带有 ProductID kly457 和 80 多个!他们都有一个名为 ProductINFO 的表。我要制作:

ProductID ID group otherstuff
shj637 1 Q2 ghafdj...
shj637 2 Q4 jhsdf...
shj637 3 Q1 jhfdl...
kly457 1 Q2 jshfd...
kly457 2 Q4 uiepc...
kly457 3 Q1 qapfn..
.. for all 80 ProductID schemas

这是表结构:

CREATE TABLE `productinfo` (
`ID` int(11) DEFAULT NULL, 
`Question` text, 
`Product Answer` double DEFAULT NULL, 
`Product Code/Script (If Required)` test, 
`Product Score/Mark` double DEFAULT NULL, 
`Feedback` text, 
`ProductID` text, 
`database ID` text) 
ENGINE=MyISAM DEFAULT CHARSET=latin1

提前谢谢你!

【问题讨论】:

  • 模式表结构是什么样的?我假设所有 80 多个都是相同的。你能在其中一个模式上运行SHOW CREATE TABLE table_name; 并发布结果into your question吗?
  • @FaNo_FN 是的,所有 80 多个模式都具有与我添加到问题中的结构相同的表。我只想将所有包含此表的模式合并。
  • 您可以将创建表语法发布为文本而不是图像吗?这样我就可以轻松地复制和粘贴它并尝试创建场景。另外,我猜你想要一些东西(可能是一个查询),它可以扫描并找到所有要包含的表,而不必对表名进行硬编码,对吗?可能是动态查询?
  • @FaNo_FN 谢谢。修改它。是的;我想要的是获取所有具有相同名称的表并在数据框中有一个 UNION 但我仍然会有 SchemaID 或者在这种情况下是 ProductID 及其关联的行。正如您所说,我不想为所有 80 多个具有相同表名的模式硬编码此过程。
  • 我可能会建议一个适用于 MySQL 的查询。巧合的是,我一直在使用PREPARED STATEMENT 构建动态查询,这将适用于大多数 MySQL 版本。我不熟悉R,所以我不确定你如何在那里使用它。如果你想试试,我可以提出建议。

标签: mysql sql r


【解决方案1】:

我认为您可以在 R 中直接将查询与用户创建的数据库/模式名称向量一起粘贴。

从名为 "quux" 的表中手动选择两个数据库 "mt1""mt2" 将是:

dbGetQuery(mycon, "select * from mt1.quux")
#       row_names  mpg cyl disp  hp drat    wt  qsec vs am gear carb
# 1     Mazda RX4 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
# 2 Mazda RX4 Wag 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
# 3    Datsun 710 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
dbGetQuery(mycon, "select * from mt2.quux")
#           row_names  mpg cyl disp  hp drat    wt  qsec vs am gear carb
# 1    Hornet 4 Drive 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
# 2 Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
# 3           Valiant 18.1   6  225 105 2.76 3.460 20.22  1  0    3    1
dbGetQuery(mycon, "(select * from mt1.quux) union all (select * from mt2.quux)")
#           row_names  mpg cyl disp  hp drat    wt  qsec vs am gear carb
# 1         Mazda RX4 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
# 2     Mazda RX4 Wag 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
# 3        Datsun 710 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
# 4    Hornet 4 Drive 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
# 5 Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
# 6           Valiant 18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

要以编程方式执行此操作,请使用

schemas <- c("mt1", "mt2")
qry <- paste(sprintf("(select * from %s.quux)", schemas), collapse = " union all ")
dbGetQuery(mycon, qry)
#           row_names  mpg cyl disp  hp drat    wt  qsec vs am gear carb
# 1         Mazda RX4 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
# 2     Mazda RX4 Wag 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
# 3        Datsun 710 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
# 4    Hornet 4 Drive 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
# 5 Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
# 6           Valiant 18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

(PS:这是为了演示使用已知模式名称的向量从一个连接中使用schema.table 方法。许多人认为select * ... 的使用有点草率,我在这里使用它简洁。)


设置

我正在使用 docker 和 mysql:8。从命令行(不是 R):

$ docker run --name some-mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:8

在 R 中:

mycon <- DBI::dbConnect(odbc::odbc(), driver="MySQL ODBC 8.0 ANSI Driver",
                        host="127.0.0.1", port=3306, user="root", password="my-secret-pw")
# initially, no database
dbGetQuery(mycon, "select database()") # NA?
#   database()
# 1       <NA>

dbExecute(mycon, "create schema mt1")
dbExecute(mycon, "create schema mt2")

dbGetQuery(mycon, "select schema_name from information_schema.schemata")
#          SCHEMA_NAME
# 1              mysql
# 2 information_schema
# 3 performance_schema
# 4                sys
# 5                mt1
# 6                mt2

dbExecute(mycon, "use mt1")
# [1] 0
dbGetQuery(mycon, "select database()")
#   database()
# 1        mt1
dbWriteTable(mycon, "quux", mtcars[1:3,], create = TRUE)

dbExecute(mycon, "use mt2")
# [1] 0
dbGetQuery(mycon, "select database()")
#   database()
# 1        mt2
dbWriteTable(mycon, "quux", mtcars[4:6,], create = TRUE)

【讨论】:

  • 非常感谢!!那很有趣。我只是不知道如何使用 80 多个具有不同名称的模式来做到这一点,这些模式将具有相同的表名。使用这种方法,我仍然必须为所有模式编写代码,对吗?或不?您将如何为大量模式编写代码?自动化整个数据加载?
  • 更改我的 schemas 变量以包含 80 多个模式,然后 paste(sprintf(..), collapse=" union all") 负责其余的,无论是 2 个还是 200 个模式。您可以手动创建schemas 对象(全部输入),或者您可以使用"select schema_name..." 查询来查找它们(通过一些过滤)。无论哪种方式,我认为答案清楚地表明了如何以编程方式执行此操作,并且不必必须单独为每个模式输入查询。
【解决方案2】:

在 SO 中,我已经看到并学习了至少一种可行的方法来解决动态列或表甚至 MySQL 数据库的问题。该函数名为PREPARED STATEMENT

以这个查询为例:

/*1*/
SET @sql := NULL;

/*2*/
SET @sql := (SELECT GROUP_CONCAT(
           CONCAT('SELECT ID, ProductID, Name, ProductGroup FROM ',table_name) 
           SEPARATOR ' UNION ALL ') FROM information_schema.tables
WHERE table_schema='my_database_name' 
/*[AND table_name ...  another condition to get only the tables you want]*/;

/*For checking purpose only. Not part of the prepared statement. Can be removed.*/
SELECT @sql;

/*3*/
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

我会根据数字一一解释:

  1. 第一步是确定一个变量。在此示例中,我使用 @sql 并将其设置为 NULL,以防万一之前使用过该变量并且仍然保留一些值。
  2. 在这里,我们将使用查询设置@sql 变量,稍后将用于执行。该查询是根据CONCATGROUP_CONCAT 的组合从information_schema 数据库的查询中创建的。这就是获取表名并与最终查询连接的方式。请注意,/*[AND table_name .. 部分是您必须定义表标识的地方。比如:
  • 例如,如果这些表具有部分匹配的名称,例如 tablename_shj637, tablename_jsfh748, tablename_hasd548 ..,则条件可能是:
AND table_name LIKE 'tablename%'
  • 如果数据库 (table_schema) 包含所有需要的表,包括 productinfo 表,那么条件可能是:
AND table_name != 'productinfo'

最终生成的@sql 看起来像这样:

SELECT ID, ProductID, Name, ProductGroup FROM hasd548 
UNION ALL 
SELECT ID, ProductID, Name, ProductGroup FROM jsfh748 
UNION ALL 
SELECT ID, ProductID, Name, ProductGroup FROM shj637

您可以在第二步之后通过运行SELECT @sql 来检查@sql 变量值。这将有助于查看查询构造是否正确。

  1. 最后一部分是PREPARE, EXECUTE然后DEALLOCATE准备好的语句。

我在这里做了一个小提琴:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=be294ef5fd46ed6e04f8b03d33f34ca2。但是,SET @sql 查询有点不同,因为小提琴条件(临时数据库、临时表)。

【讨论】:

  • 非常感谢@FaNo_FN,太棒了!我可以问一个问题来扩展您在 MySQL 中的建议吗?你能以我可以将其保存的方式编辑 sql 代码吗?例如,在表名 ProductInfo 下名为 AAPP 的架构或具有此名称的新表?再次感谢!
  • 你能发布一个你希望得到的最终sql代码的例子吗?
  • 您在 sql 中发布的查询是我想在 R 中获得的。但是,我想知道如何将查询的输出保存在 mysql 中。我是否必须使用:INSERT INTO TBALE,然后使用您建议的代码?如何保存准备好的查询的输出?
  • 要在准备好的语句中添加INSERT 语句,可以在@sql 变量语法中的GROUP_CONCAT 部分之前将其连接起来。看这个小提琴例子dbfiddle.uk/…
  • 这是我在 MySQL 中得到的错误:错误代码:1064。您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 1 行的“NULL”附近使用正确的语法。执行此操作时出现此错误:/*3*/ PREPARE stmt FROM @sql;执行语句; DEALLOCATE PREPARE stmt;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-26
  • 2017-08-29
  • 2016-11-28
  • 2020-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多