Filter OR condition / additive result / full-custom WHERE clause

Iโ€™m looking for a way to have my Exploreโ€™s generated SQL include a WHERE clause that includes OR logic.

This wouldnโ€™t be:

... WHERE (field1 = 'user_value') AND (field2 = 'hard-coded value 1' OR field3 = 'hard-coded value 2'

Rather, it would be:

... WHERE (field1 = 'user_value') OR (whatever-I-want)

The documentation for sql_where states:

If you forgot to add the parentheses in this example, and a user added their own filter, the generated WHERE clause could have the form:

WHERE
user_filter = 'something' AND
region = 'Northeast' OR
company = 'Periaptly'

In this situation, the filter that the user applied may not work. No matter what, rows with company = 'Periaptly' will show up, because the AND condition is evaluated first. Without parentheses, only part of the sql_where condition combines with the userโ€™s filter.

Thatโ€™s exactly what I want!! But neither sql_where nor sql_always_where behave that way in reality!

Is there any other way to get the behavior I want? I want to fully override the SQL WHERE clause with a custom behavior of my choosing. I have some pre-filtered derived tables, each containing rows that are filtered according to table-specific fields, and I essentially want to show all rows where the table-specific filters are met. Columns not appearing in one table at all will show up as NULL in the final results; I am OK with that, itโ€™s the expected behavior โ€“ any filters on that field should only limit results that come from that tableโ€™s source.

The only way I can think to achieve the desired behavior is to switch to using Liquid for EVERYTHING, and having separate dimensions for both โ€œthings to include in the result tableโ€ (can_filter:no) and filter-only fields (Liquid) and manually applying all of the filters by hand. Thatโ€™s neither very good for maintainability nor clear to the user (โ€œwhy canโ€™t I just filter on X like Iโ€™m used to?โ€)โ€ฆ There must be a better way.

I saw some google results for a post titled โ€œfiltering between multiple fields with or using a yesno dimensionโ€, but it looks like that post is no longer available. Not sure if itโ€™d help or notโ€ฆ

What I essentially want to do is include an โ€œOR TRUEโ€ (no parenthesis) in the main queryโ€™s WHERE clause. Having the end-user use a custom filter with the relevant OR logic is not a viable optionโ€ฆ The OR is implied by the nature of what Iโ€™m doing.

I could make some kind of a join where each row includes the results from its โ€œsource tableโ€ plus the results from the most recent [matching] row of each other table, but thatโ€™s both non-performant and confusing to the user: NULL is more straightforward to the user than including some โ€œnearby dataโ€.

Thanks in advance for any help!

1 6 9,834
6 REPLIES 6

you can do this with the following โ€œtrickโ€

Little known trick: if you use a filter-type field, you can define a very custom SQL for it that will be used in the where clause. filter: department { type: string suggest_dimension: company_department sql: {% condition department %} ${company_department} {% endcondition %} OR {% condition department %} ${product_owner} {% endcondition %} ;; } (The department inside of the {% condition %} tag may be optional when a field is referring to itโ€™s own filter selection, I forget, buโ€ฆ

Thanks @moebe! I gave that a try, making the following filter in my base View:

  filter: include_or_true {
    sql: OR TRUE ;;
  }

Resulting SQL with one user-defined filter:

WHERE (source_1 = 'user-input-here') AND (OR TRUE)

Any other ideas? I wonder if this behavior was changed in a recent Looker release maybeโ€ฆ?

This is still extremely nasty, but you can, I think, make use of the precendence of ORs vs ANDs in SQL, in combination with the fact that the `sql_always_where` seems to be the last clause in the generated sql.

explore: mytable {
  sql_always_where: /*hackstart*/ TRUE) OR ${mytable.field} = "something" AND (TRUE /*hackend*/;;
   ...

This then produces something like this.... (returns a row for both 42 and -42)

SELECT x
FROM UNNEST([-42, 9, 42]) AS x
WHERE x > 0 AND x = 42 AND (/*hackstart*/ TRUE) OR x = -42 AND (TRUE /*hackend*/)

 here the UNNEST(...) AS x is a dummy table for demonstration purposes, and the x > 0 andx = 42  bits are regular filters applied in the query.

Out of desperation, Iโ€™m considering going with BigQueryโ€™s SELECT * REPLACE(...) syntax, and injecting this SQL as the first statement in the Exploreโ€™s SELECT clause:

BigQuery documentation:

SELECT * REPLACE

A SELECT * REPLACE statement specifies one or more expression AS identifier clauses. Each identifier must match a column name from the SELECT * statement. In the output column list, the column that matches the identifier in a REPLACE clause is replaced by the expression in that REPLACE clause.

A SELECT * REPLACE statement does not change the names or order of columns. However, it can change the value and the value type.

WITH orders AS 
(SELECT 5 as order_id, 
"sprocket" as item_name,
200 as quantity)
SELECT * REPLACE ("widget" AS item_name)
FROM orders;

Soโ€ฆ : I would define a dummy dimension and force it to be selected in every query, like:

# leading underscore to try to make this always at the top
dimension: _sql_inject {
    can_filter: no
   # Below: have to figure out a way to include "WHERE source!=source_1"
    sql: * REPLACE (NULL as source_1), NULL ;;
}

Then Iโ€™d LEFT JOIN data that I am certain would match the filter for source_1, to trick the WHERE clause, but it wouldnโ€™t show up and confuse the user, because Iโ€™d replace it back with NULL above.

Terribly convoluted; I really donโ€™t want to do this, itโ€™d be a wretched hack even for me ๐Ÿ˜‰

Have to decide which is easier for me / clearer to the user: some hack like that, or Liquid for every filterable field in my projectโ€ฆ Like I said, there must be a better way to get something as basic as a logical OR conditionโ€ฆ?

Well, I figured out one hack thatโ€™s slightly less awful than the above one ๐Ÿ˜‰

explore: my_explore {
  from: base_view
  always_filter: {
    filters: [my_explore.end_block_comment: "sql-injection"]
  }

  join: first_joined_view {
    ...
    sql_where: TRUE /* ;;
  }
  ...
}

view: base_view {
   ...
  filter: end_block_comment {
    sql: */ ;;
  }
}

It messed up the highlighting in the IDE, and itโ€™s probably super fragile, but it โ€œseems to workโ€ and stops user-applied filters from getting applied to the main queryโ€ฆ Still looking for suggestions on better ways, though!

Hi,

I also had similar requirement for a global OR condition and problem was that Looker was generating chained AND conditions with brackets.

All I wanted is a global OR condition ,as left outer join on a two tables generated NULLs and I wanted the filter to be applied in the following way

`( regions.name IS NULL OR regions.name = <filtered_value> )`

The approach I did is as follows

 

 

view main {

dimension: region {
  type: string
  sql: ${TABLE}.region
  can_filter: no
}

filter: region_filter {
  type:  string
  suggest_dimension: region
  full_suggestions: yes
  sql:  ${main.name} IS NULL OR {% condition region_filter %} ${main.name} {% endcondition %};;
}
}

 

 

 Now we will expose the region_filter on dashbaords and the best thing was that if we give suggest_dimension the Links between various filters was working fine even when defined as filter

Thanks,

Sarath

Top Labels in this Space
Top Solution Authors