使用Perl将简化的SQL查询解析为SQLite

时间:2012-11-06 00:17:03

标签: sql sqlite parsing

我正在尝试将一个“简化的”SQL查询转换为一个用于对抗XnViews数据库的SQLite,这意味着数据库布局对于我正在尝试做的事情来说至多不是最理想的而且我无法改变任何关于

示例为“(cat_10和cat_12)和(cat_5或cat_7)”。

这应该用于表“t3”,其中包含“if”(fileID)和“ic”(categoryID)字段。

条目如下所示:

if, ic
7, 10
7, 12
7, 4
9, 10
9, 12
9, 5
10, 10
10, 12
10, 7

上面简化的查询应该只选择文件9和10,因为7确实有想要的类别10和12但是既没有5也没有7。

现在的实际问题是建立一个查询语句的地狱,因为我花了几个小时来简单地让两个类别之间的AND工作。

SELECT if FROM t3 WHERE ic IN (10, 12) GROUP BY if HAVING count(if) = 2

这为我提供了包含类别10和12的所有文件ID,但我不知道如何将其与剩余的“和(cat_5或cat_7)”结合起来。

当我计划这些简化的sql语句(由html和js制作的click-together-together-builder制作)时,我打算简单地用“t3.ic = 5”替换“cat_5”并将其余部分保留为是

当然,我并没有预见它不会起作用,因为在整体上检查条目并且不能有ic = 5 AND ic = 7.这几乎打破了一切。

所以我想知道是否有人会知道如何在实际工作中翻译这些简单查询,请记住它可能不限于(x和y)对。

编辑:我找到了如何做我给出的例子,我想至少:

SELECT if FROM t3 WHERE ic IN (10, 12) GROUP BY if HAVING count(if) = 2
INTERSECT 
SELECT if FROM t3 WHERE ic IN (5, 7) GROUP BY if

但现在的主要问题是以正确的顺序解决()。

编辑2:我认为我正在尝试使用group_concat()将类别分组到一个字段中,然后我应该能够简单地将猫分类为“”并且这将是我可以轻松拼凑的小块,然后只是括号,它应该工作。突出'应该'。

4 个答案:

答案 0 :(得分:2)

您的原始查询无法执行预期操作。即使您在10和10中再次WHERE ic IN (10, 12) GROUP BY if HAVING count(if) = 2但根本不是12,ic也会产生正确的结果。这违背了您对所需内容的文字描述。这是需要获取12和10的结果的内部查询的地方。您可以在我在下面发布的小提琴链接中测试您的查询失败。

有点棘手,但这就是我直截了当地解释它的方式。

SELECT DISTINCT ifc
FROM   t3
WHERE  ifc IN (
               SELECT   ifc
               FROM     t3 
               WHERE    ic = 10  
               GROUP BY ifc
               HAVING   COUNT(*) > 0             

               INTERSECT

               SELECT   ifc
               FROM     t3 
               WHERE    ic = 12
               GROUP BY ifc
               HAVING   COUNT(*) > 0
              )            
AND ic IN (5, 7)

Try fiddle

我没有带来任何优化,你可以试试你的。小提琴链接是Postgres,但这应该有效(没有让SQLite在我的浏览器中工作:()

修改: CL。指出一个有趣的事情,即不必在内部查询中包含HAVING子句,这是真的。我在SQL术语中解释OP的要求,意图在不考虑任何优化的情况下使事情变得清晰。

这是一个更好看的查询:

SELECT DISTINCT ifc
FROM   t3
WHERE  ifc IN (
               SELECT   ifc
               FROM     t3 
               WHERE    ic = 10            

               INTERSECT

               SELECT   ifc
               FROM     t3 
               WHERE    ic = 12
              )            
AND ic IN (5, 7)

答案 1 :(得分:0)

好的,我的工作正如我原先计划的那样令人惊讶。

SELECT Folders.Pathname || Images.Filename AS File FROM Images
JOIN Folders ON Images.FolderID = Folders.FolderID
LEFT JOIN (
    SELECT f, Cats, t4.if AS Tagged FROM t2
    JOIN (
        SELECT if, ' ' || group_concat(ic,' ') || ' ' AS Cats FROM t3 GROUP BY if
    ) st3 ON t2.i = st3.if
    LEFT JOIN t4 ON t2.i = t4.if
) st2 ON File = st2.f
$selectWhereImage $sqlqry
ORDER BY ModifiedDate $order LIMIT $offset, $limit

我知道这是一个查询的地狱,但它结合了我要查找的所有内容(类别ID,标记或不标记,评级,颜色),按日期排序,结果是完整的文件路径。

这可能是一种可怕的方式,但如果有人找到一个更好的工作方式,我可以简单地替换像“cat_5”这样的占位符,同时保持其余部分就像它一样,需要括号和操作符,那么我全都耳朵: d

哦和$ selectWhereImage只包含一个较长的WHERE限制文件以imageformat结尾,$ sqlqry是上面的refittet, cat_5 只会变成 cat LIKE'% 5%',由于猫的左右两侧有额外的空格我可以匹配任何数字而不会在“10”中找到“1”,因为“1”不在“10”中:D

答案 2 :(得分:0)

一种更简单的hackish方法,我相信更快

SELECT DISTINCT ifc
FROM   t3
WHERE  ifc IN (
               SELECT   ifc
               FROM     t3 
               WHERE    ic = 10
              ) 
   AND ifc IN (
               SELECT   ifc
               FROM     t3 
               WHERE    ic = 12
              )               
AND ic IN (5, 7)

答案 3 :(得分:0)

如果你必须像你一样使用交叉,你应该改变你的上层查询是错误的。由于您必须确保每个if都有10和12为ic,因此如果没有两个单独的查询,您就无法逃脱。类似的东西:

SELECT ifc
FROM   t3
WHERE  ifc IN (
               SELECT   ifc
               FROM     t3 
               WHERE    ic = 10
              ) 
   AND ifc IN (
               SELECT   ifc
               FROM     t3 
               WHERE    ic = 12
              )

INTERSECT

SELECT ifc FROM t3 WHERE ic IN (5, 7)

INTERSECT将在此处理该组,因此您不必显式添加,但这不会像我的其他查询一样有效。如果必须使用子查询,可以使用JOIN

SELECT DISTINCT t.ifc
FROM   t3 AS t
JOIN   t3 AS v ON v.ifc = t.ifc
JOIN   t3 AS p ON p.ifc = t.ifc
WHERE  v.ic = 10 AND p.ic = 12 AND t.ic IN (5, 7)

第二个优点是它适用于不像MySQL那样知道INTERSECT的数据库。