【问题标题】:How to `SELECT FROM` a table that is a part of a query itself using MySQL?如何使用 MySQL `SELECT FROM` 作为查询本身的一部分的表?
【发布时间】:2020-04-04 04:12:30
【问题描述】:

假设我有多个具有相同架构的表:

CREATE TABLE `tbl01`
(
  `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `name` TINYTEXT,
  `data` INT
);

CREATE TABLE `tbl02`
(
  `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `name` TINYTEXT,
  `data` INT
);

CREATE TABLE `tbl03`
(
  `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `name` TINYTEXT,
  `data` INT
);

-- etc. ------------------

INSERT INTO `tbl01` (`name`, `data`) VALUES
('row 1', 1),
('row 2', 1),
('row 3', 3);

INSERT INTO `tbl02` (`name`, `data`) VALUES
('cube', 1),
('circle', 0);

INSERT INTO `tbl03` (`name`, `data`) VALUES
('one', 1);

然后是一个表,其中一列中包含所有其他表的名称:

CREATE TABLE `AllTbls`
(
  `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `tblnm` VARCHAR(64) NOT NULL UNIQUE,
  `desc` TINYTEXT,
  `flgs` BIGINT UNSIGNED
);

INSERT INTO `AllTbls` (`tblnm`, `desc`, `flgs`) VALUES
('tbl01', 'Table 1', 0),
('tbl02', 'Table two', 1),
('tbl03', '3rd table', 0);

因此,如果我想编写一个查询来检索 AllTbls 的内容,并在一列中包含每个相应表中的行数,我认为以下方法是这样做的:

SELECT *, `tblnm` as TblName, (SELECT COUNT(*) FROM TblName) as cntRws 
  FROM `AllTbls` ORDER BY `id` ASC LIMIT 0,30;

但这会返回错误:

#1146 - 表 'database.TblName' 不存在

我知道我可以在多个查询中执行此操作(使用编程语言中的循环),但是否可以在一个查询中执行此操作?

PS。我正在使用MySQL v.5.7.28

【问题讨论】:

  • 是否可以通过某些您无法控制的外部进程随时创建新表?
  • @CaiusJard 是的,他们可以随时添加。
  • 如果没有动态 SQL,您将无法做到这一点。
  • @TimBiegeleisen 和dynamic SQL 是什么?当我用一种编程语言“即时”编写它时。
  • 我认为您不能使用/插入 SELECTED 字段作为 FROM 参数。如果您探索信息模式的上层查询,也许可以?

标签: mysql select count


【解决方案1】:

简单的答案是:“你不能”

不应该像变量一样使用表名来保存数据。你应该有一张桌子:

tblContractCounts
Client, ContractCount
-------------------
IBM, 1
Microsoft, 3
Google, 2

不是三个表:

tblIBMContractCounts
ContractCount
1

tblMicrosoftContractCounts
ContractCount
3

tblGoogleContractCounts
ContractCount
2

如果您的表数量已知且固定,您或许可以通过创建一个将它们重新组合在一起的视图来解决问题,或者着手进行一项操作以将它们全部放入一个表中,并将单独的视图命名为旧名称,这样就可以了继续工作,直到你可以改变它们。如果一直添加新表,则说明数据建模存在缺陷,需要进行更正。在这种情况下,您必须使用编程语言(前端或存储过程)来构建单个查询:

//pseudo code
strSql = ""
for each row in dbquery("Select name from alltbls")
  strSql += "select '" + row.name + "' as tbl, count(*) as ct from " + row.name + " union all "
next row
strSql += "select 'dummy', 0"

result = dbquery(strSql)

不必是您的前端执行此操作 - 您也可以在 mysql 中执行此操作并利用动态 sql / EXECUTE。请参阅THIS ANSWER 我们如何使用上述逻辑连接字符串,以便字符串包含 sql 查询,然后执行查询。信息架构将为您提供获取所有当前表名列表所需的信息

但是您所做的只是解决您的数据建模被破坏的事实;我建议改为解决此问题

ps:INFORMATION_SCHEMA 对带有名称的表进行粗略计数,在这种特殊情况下可能足以满足您的需求

select table_name, table_rows from infornation_schema.tables where table_name like ...

【讨论】:

  • 感谢您的解释。
【解决方案2】:

我设法使用以下存储过程解决了这个问题。

-- DROP PROCEDURE sp_Count_Rows;
Delimiter $$
CREATE PROCEDURE sp_Count_Rows() 
BEGIN
DECLARE table_name TEXT DEFAULT "";
DECLARE finished INTEGER DEFAULT 0;
DECLARE table_cursor 
CURSOR FOR 
    SELECT tblnm FROM alltbls;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;

OPEN table_cursor;
DROP TABLE IF EXISTS RowsCount;
CREATE TABLE IF NOT EXISTS RowsCount(Tlbnm text, ctnRws int);

table_loop: LOOP

    FETCH table_cursor INTO table_name;

    IF finished = 1 THEN 
        LEAVE table_loop;
    END IF;

    SET @s = CONCAT("insert into RowsCount select '", table_name ,"', count(*) as cntRws from ", table_name);
    PREPARE stmt1 FROM @s; 
    EXECUTE stmt1; 
    DEALLOCATE PREPARE stmt1; 

END LOOP table_loop;
CLOSE table_cursor;

SELECT * FROM RowsCount;
DROP TABLE RowsCount;
END
$$

然后当你调用程序时

CALL sp_Count_Rows();

You get this result

【讨论】:

  • 谢谢。我将不得不审查它。不幸的是,我的 MySQL 版本只有5.7.28。是什么让它依赖于 v.8?
  • 我不知道具体的信息,但这是为了以防您必须对代码进行修改,或者数据库管理器可能会发出一些警告/问题。
猜你喜欢
  • 1970-01-01
  • 2015-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-30
  • 2019-05-14
  • 2015-05-15
  • 2012-07-14
相关资源
最近更新 更多