动态SQL替代方案(MySql)

时间:2017-01-14 22:20:19

标签: mysql stored-procedures

在当前的MySql数据库服务器上,我有两个模式:“朋友”,“地方”。整个数据库围绕从外部调用的存储过程进行组织。也许这是好方法,也许它很糟糕,但它与我遇到的这个问题无关。在这种情况下,DB必须与使用它的任何外部软件分开(因为我只负责DB)。

“朋友”架构中的某些存储过程引用“位置”中的表格,反之亦然。现在,如果我想在同一台服务器上设置新的模式集,但是对于另一个像这样的“客户端”:

Friends_clientOne
Places_clientOne
Friends_clientTwo
Places_clientTwo

我遇到了问题 - 从另一个模式引用表的存储过程将不知道要使用哪个模式名称。每次创建新集时,检查并修改每个过程以适应适当的模式名称都不是一种选择。动态SQL对我来说是全新的 - 还有什么其他选择?例如,我怎么能这样做:

(stored procedure inside schema Friends_clientOne):
Select * from Places_<getCurrentSchemaSuffix>.someTable;

请告诉我MySql足够灵活:( Percona怎么样?

2 个答案:

答案 0 :(得分:2)

与您描述的内容最接近的是内置函数DATABASE()http://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_database),它返回当前的默认数据库。

默认数据库不一定是给定表所属的数据库。它是最近在USE <databasename>语句中命名的数据库。如果您可以依赖您的应用程序始终使用您所属的数据库,那么您可以使用该功能。

但是,没有SQL实现允许您在查询执行期间动态更改表名。您可以在执行语句准备之前仅为命名表,并将其硬编码到查询中。没有使表名变量的语法。

即使您使用DATABASE()函数,也必须使用动态SQL。

Percona Server与此问题的Oracle MySQL没有什么区别。

您遇到此问题的选项是:

  1. 停止为每个客户端使用多个架构。将每个客户端的所有数据放入单个模式中。这似乎最简单。

  2. 将存储过程设计为每个客户端都是唯一的。你说你不想这样做。但是对于它的价值,我们在我目前工作中管理的客户数据库中的存储过程和触发器中执行此操作。没那么糟糕。我们为每个触发器或过程提供了CREATE语句的“模板”版本,并为客户ID提供了占位符令牌。当我们创建一个新客户的数据库时,我们复制该模板代码并在客户ID占位符上进行替换,然后运行它。

  3. 将您的每个客户端数据放入他们自己的MySQL服务器专用实例中。这样,每个客户端可以有多个模式,但每个客户端的模式名称不需要是不同的。您可以在一台服务器主机上运行多个实例,只需配置不同的datadir,port,sock_file和其他日志文件即可。虽然我已经看过这个解决方案,但我不推荐它,因为它有很多资源开销,而且很难管理。

  4. 学习使用动态SQL。

答案 1 :(得分:1)

您可以在Procs中使用PREPARED语句:

DELIMITER //

CREATE PROCEDURE getPlace (OUT param1 char)
 BEGIN
    SELECT CONCAT("Select * from Places_", SUBSTRING_INDEX(DATABASE(), '_', -1),".someTable;") INTO @sql;
    PREPARE getPlaces from  @sql;
    EXECUTE getPlaces;
    DEALLOCATE PREPARE getPlaces;
 END;
//

DELIMITER ;

<强>样品

MariaDB [mysql]> CREATE DATABASE Friends_clientOne;
Query OK, 1 row affected (0.00 sec)

MariaDB [mysql]> CREATE DATABASE Friends_clientTwo;
Query OK, 1 row affected (0.00 sec)

MariaDB [mysql]> CREATE DATABASE Places_clientOne;
Query OK, 1 row affected (0.00 sec)

MariaDB [mysql]> CREATE DATABASE Places_clientTWO;
Query OK, 1 row affected (0.00 sec)

MariaDB [mysql]> CREATE TABLE Places_clientOne.someTable (name varchar(32));
Query OK, 0 rows affected (0.02 sec)

MariaDB [mysql]> CREATE TABLE Places_clientTwo.someTable (name varchar(32));
Query OK, 0 rows affected (0.02 sec)

MariaDB [mysql]> INSERT INTO Places_clientOne.someTable VALUES('text in Places_clientOne.someTable');
Query OK, 1 row affected, 1 warning (0.00 sec)

MariaDB [mysql]> INSERT INTO Places_clientTwo.someTable VALUES('text in Places_clientTwo.someTable');
Query OK, 1 row affected, 1 warning (0.01 sec)

MariaDB [mysql]> use Friends_clientOne;
Database changed
MariaDB [Friends_clientOne]> DELIMITER //
MariaDB [Friends_clientOne]> CREATE PROCEDURE getPlace (OUT param1 char)
    ->  BEGIN
    ->     SELECT CONCAT("Select * from Places_", SUBSTRING_INDEX(DATABASE(), '_', -1),".someTable;") INTO @sql;
    ->     PREPARE getPlaces from  @sql;
    ->     EXECUTE getPlaces;
    ->     DEALLOCATE PREPARE getPlaces;
    ->  END;
    -> //
Query OK, 0 rows affected (0.03 sec)
MariaDB [Friends_clientOne]> DELIMITER ;

MariaDB [(none)]> use Friends_clientTwo;
Database changed
MariaDB [Friends_clientTwo]> DELIMITER //
MariaDB [Friends_clientTwo]>
MariaDB [Friends_clientTwo]> CREATE PROCEDURE getPlace (OUT param1 char)
    ->  BEGIN
    ->     SELECT CONCAT("Select * from Places_", SUBSTRING_INDEX(DATABASE(), '_', -1),".someTable;") INTO @sql;
    ->     PREPARE getPlaces from  @sql;
    ->     EXECUTE getPlaces;
    ->     DEALLOCATE PREPARE getPlaces;
    ->  END;
    -> //
Query OK, 0 rows affected (0.02 sec)

MariaDB [Friends_clientTwo]> DELIMITER ;
MariaDB [Friends_clientTwo]> call getPlace(@r);
+----------------------------------+
| name                             |
+----------------------------------+
| text in Places_clientTwo.someTab |
+----------------------------------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

MariaDB [Friends_clientTwo]> use Friends_clientOne;
Database changed
MariaDB [Friends_clientOne]> call getPlace(@r);
+----------------------------------+
| name                             |
+----------------------------------+
| text in Places_clientOne.someTab |
+----------------------------------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

MariaDB [Friends_clientOne]>