I created a postgres function that receives an array of text as parameter. For each text in the array I want to check if any of the the columns in the query match it with like clause.
Is it possible to aggregate where clause like this in a for loop?
For example for clarification, I have the above tables:
lessons
---- ----------- -----------------
| id | name | teacher_id |
---- ----------- -----------------
| 1 | English | 1 |
---- ----------- -----------------
| 2 | Spanish | 2 |
---- ----------- -----------------
Teachers
---- -----------
| id | name |
---- -----------
| 1 | Teacher 1 |
---- -----------
| 2 | Teacher 2 |
---- -----------
I want to get as parameters text[] and for each text in the array I want to execute an OR clause between the column in both tables and return array of jsons([{"id": 1, "teacher": {"id":1, "name": "Teacher1"}}]
)
For example if the parameters are ["lish", "er"] I want it to execute:
where
lessons.name like '%lish%' or teachers.name like '%lish%'
and
lessons.name like '%er%' or teachers.name like '%er%'
teacher with id 1 will return.
The thing is I don't know in advance the parameters so this is why I assume I need a FOR loop.
Also, how to I make the query to return an array of jsons that each teacher will be an inner json object of each lesson?
Would appreciate some examples if so. Thank you!
CodePudding user response:
You don't need a loop-like function such as in plpgsql to get your expected result. A basic sql
query will be more efficient :
SELECT l.id, l.name, t.id, t.name
FROM lessons AS l
INNER JOIN teachers AS t
ON t.id = l.teacher_id
INNER JOIN unnest( array['lish', 'er']) WITH ORDINALITY AS c(cond, id)
ON t.name ~ c.cond OR l.name ~ c.cond
GROUP BY l.id, l.name, t.id, t.name
HAVING count(*) = max(c.id)
- First this query joins both tables
lessons
andteachers
with a match onteacher_id
. - Then it is joined with every element of the
input array through the
unnest
function. - For each row of the joined tables
lessons
andteachers
, both names are compared to the input array element using the regular expression~
operator. - Then the selected rows are grouped together so that to count the number of
rows which match with all the array elements. This check is performed
in the
HAVING
clause wherecount(*)
is the number of resulting rows for the same (lessons.id, teachers.id) tuple and it is compared to the number of elements in the arraymax(c.id)
If you want to return an array of jsonb, just format the query result :
SELECT json_agg(c.result)
FROM (
SELECT (json_build_object('lesson_id', l.id, 'lesson_name',l.name, 'teacher', json_build_object('teacher_id', t.id, 'teacher_name', t.name))) AS result
FROM lessons AS l
INNER JOIN teachers AS t
ON t.id = l.teacher_id
INNER JOIN unnest( array['lish', 'er']) WITH ORDINALITY AS c(cond, id)
ON t.name ~ c.cond OR l.name ~ c.cond
GROUP BY l.id, l.name, t.id, t.name
HAVING count(*) = max(c.id)) AS c
see the full test result in dbfiddle