Find entry where value does not intersect with other value

一世执手 提交于 2020-01-07 03:51:24

问题


I am having issues making an ActiveRecord query in Ruby on Rails that gives me back all users, whose blocked_dates (autogenerated string value) do not intersect with the formatted_dates (autogenerated string value) of a given event. The problem I'm having is that, for example:

User.where.not("string_to_array(blocked_dates, ',') && string_to_array(?, ',')", "26.12.2015")

Gives back an empty list, whereby:

User.where("string_to_array(blocked_dates, ',') && string_to_array(?, ',')", "26.12.2015")

Gives back the correct users whose blocked_dates actually contain '26.12.2015'.

Is there a reason for this strange behavior? Or does the Postgres overlap operator && not work in conjunction with NOT?

In case the question arises, here is the generated SQL query:

SELECT "users".* FROM "users"  WHERE (NOT (string_to_array(blocked_dates, ',') && string_to_array('26.12.2015', ',')))

回答1:


Ok, I think I understood the problem I had. The default value of "blocked_dates" of the users table was nil. Because of that, the query was not able to calculate overlaps. After I changed the default value of blocked_dates to "" instead of nil, the NOT statement started giving me the correct values.




回答2:


You must be aware that only WHERE clause expressions evaluating to TRUE qualify. When inverting a boolean value with NOT, NULL stays NULL and still doesn't qualify. You can use NULL-safe constructs like:

WHERE  (string_to_array(blocked_dates, ',')
     @> string_to_array('26.12.2015', ',')) IS NOT TRUE

(Using the simpler contains operator @> for your case testing for a single date, btw.)

Or:

WHERE  (blocked_dates IS NULL OR
        NOT (string_to_array(blocked_dates, ',') @> string_to_array('26.12.2015', ','))

Or, while working with your awkward string:

WHERE  (blocked_dates LIKE '%26.12.2015%') IS NOT TRUE

But all of this is putting lipstick on a pig and all the constructs are error-prone and depend on a (hopefully) matching date format. Why the string-to-array conversion in the first place? The column blocked_dates should at least be an array of dates (date[]) or, better yet, normalize your relational model with a separate table listing blocked dates instead of the column users.blocked_dates:

CREATE TABLE user_blocked_date (
  user_id int REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE
, blocked_date date
, PRIMARY KEY (user_id, blocked_date)
);

Depending on data distribution this may or may not occupy more space on disk. But your query would be much faster with one of the standard techniques like:

SELECT *
FROM   users u
WHERE  NOT EXISTS (
   SELECT 1
   FROM   user_blocked_date
   WHERE  user_id = u.user_id
   AND    blocked_date = '2015-12-26';

Using ISO 8601 date format, btw. Why?

  • How to convert "string" to "timestamp without time zone"

Related:

  • Select rows which are not present in other table


来源:https://stackoverflow.com/questions/34810819/find-entry-where-value-does-not-intersect-with-other-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!