1

I have to delete rows in a table based on multiple comparisons with a really long function application on one of the columns. To go a bit into details, i'm decoding from base64, doing a substring, transforming to json, taking a specific field, then transforming that to inet.

I'm using those ips to compare with other ips / masks in my WHERE clause.

The problem i have is that obtaining the actual result is a pretty long string, and if i end up saying ip < foo(bar(baz(quux))) OR ip2 < foo(bar(baz(quux))) then that value for comparison will end up being calculated multiple times too.

I'm looking for a way to precompute that value, assign it a meaningful name, then use that name in my WHERE clause.

Thank you

2 Answers 2

2

You can use a lateral join:

select . . .
from t cross join lateral
     (values (foo(bar(baz(quux))) ) v(decoded_value)
where ip < v.decoded_vaue or ip2 < v.decoded_value;

In Postgres, a delete is a little trickier. Assuming you have a primary key:

delete t
using t t2 cross join lateral
     (values (foo(bar(baz(t2.quux))) ) v(decoded_value)
where t2.primary_key = t.primary_key and
      (t2.ip < v.decoded_vaue or t2.ip2 < v.decoded_value);
Sign up to request clarification or add additional context in comments.

2 Comments

I'm doing a DELETE, not a select, so would using a join not actually have me end up with a different table / view ?
That second one will do it i think. Thank you.
1

Have you considered the usage of a CTE?

For example, let's say that we have the following table

CREATE TABLE test_table (
  id BIGINT PRIMARY KEY,
  some_data INTEGER
);

Let's say that we want to delete all of the rows in which the result of the expression

some_data % 10

is (>=1 and <=4) or (>=6 and <=8).

In this case the modulo operation replaces your foo(bar(baz(column_name))) call with the modulo operation.

The simple approach would be to write the following query:

DELETE FROM test_table
WHERE
  (MOD(some_data, 10) >= 1 AND MOD(some_data, 10) <= 4)
  OR
  (MOD(some_data, 10) >=6 AND MOD(some_data, 10) <= 8);

As you said, this will recalculate the same thing multiple times. In order to do the calculation only once, we can create a CTE and use the result in the delete query.

WITH precalculated_table AS (
  SELECT *, mod(some_data, 10) as mod_10_result FROM test_table
)
DELETE FROM test_table WHERE id IN (
    SELECT id from precalculated_table
  where
    (mod_10_result >= 1 and mod_10_result <= 4)
    OR
    (mod_10_result >= 6 AND mod_10_result <= 8)
);

This way, we do the calculation only once and then we use the precaculated column in the WHERE part of the DELETE query.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.