使用许多laterail连接建立动态查询

时间:2016-11-10 15:46:39

标签: sql postgresql entity-attribute-value

我正在根据用户指定字段的用户提交条件构建动态查询。

我有两张桌子

Visitors(id, name, email...)

Trackings(id, visitor_id, field, string_value, integer_value, boolean_value, date_value)

条件以前面构建的SQL片段数组的形式出现。 visitors table上的硬编码属性和自定义过滤器的默认过滤器,用于用户提交并存储在EAV架构中的值(跟踪)

示例:

Default:
{"name ILIKE 'Jack'", "(last_seen < (current_date - (7 || ' days')::interval))"}

Custom:
{"field = 'number_of_orders' > 10", "is_pro_user = true"}

访问者可以拥有多个跟踪,每个跟踪都会为该访问者记录一些自定义的,用户提交的数据字段。但每个访问者也有一些默认数据位于表格本身,例如emailnamelast_seen等。

现在,用户应该询问如下问题:

  • 向我提供所有未记录名为number_of_orders的自定义字段的访问者(未知)
  • 将所有访问者的默认属性name设置为Jack以及自定义属性total_purchase_value大于1000的人

我尝试解决它是使用一个存储过程,它使用AND(对于访问者表上的默认数据)和WHERE-caluse内的OR语句(对于跟踪表中的自定义数据)动态连接一系列条件

CREATE OR REPLACE FUNCTION find_matching_visitors(app_id text, default_filters text[], custom_filters text[])
RETURNS TABLE (
  id varchar
) AS
$body$
DECLARE
    default_filterstring text;
    custom_filterstring text;
    default_filter_length integer;
    custom_filter_length integer;
    sql VARCHAR;
BEGIN
    default_filter_length := COALESCE(array_length(default_filters, 1), 0);
    custom_filter_length := COALESCE(array_length(custom_filters, 1), 0);

    default_filterstring := array_to_string(default_filters, ' AND ');
    custom_filterstring := array_to_string(custom_filters, ' OR ');

    IF custom_filterstring = '' or custom_filterstring is null THEN
        custom_filterstring := '1=1';
    END IF;

    IF default_filterstring = '' or default_filterstring is null THEN
        default_filterstring := '1=1';
    END IF;

    sql := format('
                SELECT v.id FROM visitors v
                LEFT JOIN trackings t on v.id = t.visitor_id
                WHERE v.app_id = app_id and (%s) and (%s)
                group by v.id
                having case when %s > 0 then count(v.id) = %s else true end
            ', custom_filterstring, default_filterstring, custom_filter_length, custom_filter_length);
    RETURN QUERY EXECUTE sql;

END;
$body$
LANGUAGE 'plpgsql';

这很好用,但AFAIK无法表达自定义属性的unknown过滤器。因为它需要使用left outer joinnot exists子查询来访问外部范围。

我现在正在寻找完成上述相同的替代方法,但也支持这种查询。我正在考虑类似下面的内容,对每个条件使用一系列横向连接,但是这似乎只要有超过1-2个条件/连接就不能很好地执行。

select v.id, v.name from visitors v
inner join lateral ( <-- custom fields
    select * from trackings t
    where field = 'admin'
) as t1 on t1.visitor_id = v.id
inner join lateral (
    select * from trackings t
    where field = 'users_created' 
) as t2 on t2.visitor_id = v.id
inner join lateral (
    select * from trackings t
    where field = 'teams_created' and integer_value > 0
) as t3 on t3.visitor_id = v.id
where v.app_id = 'ASnYW1-RgCl0I' and (v.type = 'lead' or v.type = 'user')
and name ILIKE 'mads' and email is not null // <-- default fields

有什么建议吗?

0 个答案:

没有答案