使用algebraixlib编写复杂查询

时间:2015-09-19 10:25:09

标签: python algebraixlib

我正在尝试使用algebraixlib完成一些复杂的ETL(提取,传输,加载)过程。我有一个像这样的查询

Select column1, column2, column3, column4, date(column5), date(column6) 
FROM mytable 
WHERE column1 = some string,
AND a date value = between column5 and column6,
AND (column3 = some string OR Comunn3 = some other string),
AND (column4 = some string OR Comunn4 = some other string)

对我来说,更复杂的部分是编写彼此嵌套的ORAND操作,例如在行AND (column3 = some string OR Comunn3 = some other string)中,我不知道如何编写{{ 1}}操作后跟OR操作,然后再次使用AND继续操作,依此类推。我知道AND可用于clans.superstrictAND操作。但嵌套的OR AND操作让我无法理解。

是否有人可以帮助我使用OR将所有这些放在一个函数中。我也读过这本书,但在这件事上没什么用。

如果您需要更多解释,请与我们联系。

1 个答案:

答案 0 :(得分:1)

设置

以下是一些示例代码和解释,用于对algebraixlib中的where子句进行建模。以下是用于运行代码的Python导入:

from datetime import datetime, date
from io import StringIO
from algebraixlib.io.csv import import_csv
from algebraixlib.mathobjects import Set, Couplet, Atom
import algebraixlib.algebras.sets as sets
import algebraixlib.algebras.clans as clans
from algebraixlib.algebras.relations import is_functional

以下是一个示例CSV文件,其中包含查询中提到的列以及将其作为clan导入的代码。 (与comparing-date-object-in-a-couplet-between-two-dates中给出的答案有关)。列名称缩写。

mycsvfile = """col1,col2,col3,col4,col5,col6
foo,bar1,baz1,nurf1,date(01-01-2015),date(01-01-2016)
foo,bar2,baz2,nurf2,date(01-02-2015),date(01-02-2016)
foo,bar3,baz2,nurf3,date(01-03-2015),date(01-03-2016)
foo,bar4,baz3,nurf2,date(01-04-2015),date(01-04-2016)
notfoo,bar5,baz1,nurf1,date(01-05-2015),date(01-05-2016)
foo,bar6,baz1,nurf2,date(01-06-2998),date(01-06-2999)"""

def read_date(date_str: str) -> datetime:
    return datetime.strptime(date_str, 'date(%m-%d-%Y)').date()

# The read_date helper imports the date(...) strings as Python date objects
data_clan = import_csv(StringIO(mycsvfile),
                       {'col1': str, 'col2': str, 'col3': str, 'col5': read_date, 'col6': read_date})

这是你的SQL,用特定的常量重写。

select col1, col2, col3, col4, col5, col6
from mytable
where column1 = 'foo',
and date(04-01-2015) between col5 and col6,
and (col3 = 'baz1' or col3 = 'baz2')
and (col4 = 'nurf1' or col4 = 'nurf2')

方法#1:限制谓词

由于algebraixlib是一个Python包,因此使用内置逻辑运算符构建复杂谓词并将其包装在传递给clans.restrict的函数中相当容易。 custom_predicate将一个关系(data_clan的元素)作为输入,并返回bool

def custom_predicate(rel):
    assert is_functional(rel)
    return rel('col1') == Atom('foo') \
           and rel('col5') < Atom(date(2015, 4, 1)) < rel('col6') \
           and (rel('col3') == Atom('baz1') or rel('col3') == Atom('baz2')) \
           and (rel('col4') == Atom('nurf1') or rel('col4') == Atom('nurf2'))
answer = sets.restrict(data_clan, custom_predicate)

返回以下部落MathObject(以表格形式打印):

'col1'|'col2'|'col3'|'col4' |'col5'                   |'col6'                   |
'foo' |'bar1'|'baz1'|'nurf1'|datetime.date(2015, 1, 1)|datetime.date(2016, 1, 1)|
'foo' |'bar2'|'baz2'|'nurf2'|datetime.date(2015, 1, 2)|datetime.date(2016, 1, 2)|

方法#2 - 使用超限

您的问题暗示了更加以数据代数为中心的where子句中对等式测试进行建模的方法。

  

我知道clans.superstrict可以用于AND和OR操作。   但嵌套的AND操作我无法理解。

您可以将谓词(相等部分)转换为分离正态形式的氏族,其中每个关系为or'd,每个对联为and'd。这并不像听起来那么糟糕。将查询中的每个连词视为单个部落,描述给定列的可能值:

enter image description here

我们正在寻找的一系列分离是通过这些部族的交叉联合来实现的。

enter image description here

现在,使用 predAsClan 超限data_clan只会留下包含与where子句匹配的字符串的记录。然后可以使用更简单的谓词函数处理日期范围部分。这是代码:

vals_col1 = Set(Set(Couplet('col1', 'foo')))
vals_col3 = Set(Set(Couplet('col3', 'baz1')), Set(Couplet('col3', 'baz2')))
vals_col4 = Set(Set(Couplet('col4', 'nurf1')), Set(Couplet('col4', 'nurf2')))
pred_as_clan = clans.cross_union(vals_col1, clans.cross_union(vals_col3, vals_col4))
answer = clans.superstrict(data_clan, pred_as_clan)
answer = sets.restrict(answer, lambda rel: rel('col5') < Atom(date(2015, 4, 1)) < rel('col6'))

给出相同的答案:

'col1'|'col2'|'col3'|'col4' |'col5'                   |'col6'                   |
'foo' |'bar1'|'baz1'|'nurf1'|datetime.date(2015, 1, 1)|datetime.date(2016, 1, 1)|
'foo' |'bar2'|'baz2'|'nurf2'|datetime.date(2015, 1, 2)|datetime.date(2016, 1, 2)|