我正在尝试构建一个小型rails应用程序,以游戏格式向初学者教授sql。它的一部分是一个命令行,其中的用途试图解决sql的问题。在后端我想对测试数据库运行他们的sql命令,并检查结果以查看他们的命令是否正确。
我目前的想法是为课程的每个阶段创建sqlite数据库,然后在每个用户到达该阶段时为该数据库创建该数据库的副本。
我认为这会奏效,但我担心效率。
我的问题是,是否有一种有效的方法让用户运行SQL命令(包括drop,alter等),获得结果,但实际上并没有对db本身进行任何更改,实质上是一个任意的干运行sql命令。我熟悉sqlite和postgres,但我对其他数据库开放。
答案 0 :(得分:9)
在Postgres中,您可以在最后回滚的交易做很多事情:
BEGIN;
UPDATE foo ...:
INSERT bar ...;
SELECT baz FROM ...;
CREATE TABLE abc...; -- even works for DDL statements
DROP TABLE def...;
ALTER TABLE ghi ...:
ROLLBACK; -- !
请注意,有些事情无法回滚。例如,序列不会回滚。或者像dblink调用一些特殊命令。
某些命令无法在与他人的交易中运行。与CREATE DATABASE
或VACUUM
一样。
此外,并发加载可能会产生副作用,例如死锁。但不太可能。您可以将transaction isolation level设置为您的要求,以排除任何副作用(以一定的性能成本)。
我不会用合理的数据来做这件事。意外事故的风险太大了。让用户执行任意代码是一种难以控制的风险。但对于训练环境来说,这应该足够好了。
使用模板数据库备份。如果出现问题,这是恢复基本状态的最快方法。例子(见最后一章):
Truncating all tables in a Postgres database
这也可以用作强力替代:为每个受训者提供原始的新数据库。
答案 1 :(得分:3)
虽然你可以在一个事务中运行他们的代码,然后在回滚之前尝试检查同一事务中的结果,但这会有一些挑战。
首先,如果代码中有任何错误,则事务将中止。除了“失败,这是错误信息”之外,您将无法说出任何其他信息。特别是,您将无法在错误发生之前检查部分结果。
其次,如果你在一个数据库中进行并行运行,锁定会让你感到困惑。每个会话都将获得锁,通常是对元组或整个关系的独占锁。其他会话将阻止等待锁定。这意味着您必须连续进行测试运行。这在您的环境中可能不是问题,但需要考虑。
第三,正如Erwin所说,你必须使你的检查代码对序列值不敏感,因为它们不会在回滚时重置。
出于所有这些原因,我强烈建议:
CREATE DATABASE course_username_lesson TEMPLATE course_lesson OWNER username;
...或者省略模板数据库的使用,只需让你的Rails应用程序使用每个用户的迁移设置一个空白数据库。这可能更简单,因为这意味着您不必维护模板DB。
PostgreSQL服务器对数百个数据库非常满意,至少如果预计它不会在峰值生产负载下执行。
您可能需要查看http://sqlfiddle.com/和许多其他现有的基于Web的SQL编辑器/培训工具,其中许多都是开源的。