Reputation: 11
I have a table called sentences
, and a table called logs
.
The sentences
table looks like this:
|------------|---------------------------------------|
| id | sentence |
|------------|---------------------------------------|
| 1 | [var1] says hello! |
|------------|---------------------------------------|
| 2 | [var1] says [var2]! |
|------------|---------------------------------------|
| 3 | [var1] says [var2] and [var3]! |
|------------|---------------------------------------|
| 4 | [var4] says [var2] to [var1]! |
|------------|---------------------------------------|
The logs
table looks like this:
|------------|------------------|--------------|--------------|--------------|--------------|
| id | sentenceId | var1 | var2 | var3 | var4 |
|------------|------------------|--------------|--------------|--------------|--------------|
| 1 | 1 | Sam | | | |
|------------|------------------|--------------|--------------|--------------|--------------|
| 2 | 2 | Joe | what's up | | |
|------------|------------------|--------------|--------------|--------------|--------------|
| 3 | 3 | Tim | hey | how are you | |
|------------|------------------|--------------|--------------|--------------|--------------|
| 4 | 4 | Joe | hi | | Tiffany |
|------------|------------------|--------------|--------------|--------------|--------------|
The result I am trying to get is:
|------------|-----------------------------------------|
| logs.id | sentences.sentence |
|------------|-----------------------------------------|
| 1 | [Sam] says hello! |
|------------|-----------------------------------------|
| 2 | [Joe] says [what's up]! |
|------------|-----------------------------------------|
| 3 | [Tim] says [hey] and [how are you]! |
|------------|-----------------------------------------|
| 4 | [Tiffany] says [hi] to [Joe] |
|------------|-----------------------------------------|
I'm not sure how to write the SQL query to make the database do the text substitutions for me.
I could just select everything from both tables using an inner join, and then loop through in code and do the substitutions myself. I.e.:
SELECT logs.id, sentences.sentence, logs.var1, logs.var2, logs.var3, logs.var4 FROM logs INNER JOIN sentences ON logs.sentenceId = sentences.id
And then in code:
logs.forEach(log => log.sentence.replace(/\[(.*?)\]/g, ($matchedString, $columnName) => log[$columnName] ))
But if possible, I'd like the database to do that for me so that I don't have to select more data than I need.
Upvotes: 1
Views: 121
Reputation: 14861
Elegance is always nice, but sometimes brute force gets it done.
with logsnn (sentenceid, var1, var2, var3,var4) as
( select sentenceid
, coalesce(var1,'')
, coalesce(var2,'')
, coalesce(var3,'')
, coalesce(var4,'')
from logs
)
select s.id
,(replace(replace(replace(replace(s.sentence
, '[var1]',l.var1)
, '[var2]',l.var2)
, '[var3]',l.var3)
, '[var4]',l.var4)
) AS sentence
from sentences s
left join logsnn l
on l.sentenceid = s.id;
If you really need the brackets on the result change the replacement settings to
'[var1]','[' || l.var1 || ']')
The answer by @JNevill is close to the same, bit I believe that one will return Null if any of var1,var2,var3, or var4 are Null. The CTE here changes Null with the empty string. Postgres does not consider the empty string the same as null.
Upvotes: 0
Reputation:
I would write a function to do that:
create function replace_vars(p_sentence text, p_vars jsonb)
returns text
as
$$
declare
l_rec record;
l_result text;
begin
l_result := p_sentence;
for l_rec in select * from jsonb_each_text(jsonb_strip_nulls(p_vars)) as x(var,value)
loop
l_result := replace(l_result, l_rec.var, l_rec.value);
end loop;
return l_result;
end;
$$
language plpgsql;
Then you can use it like this:
select s.id, s.sentence, replace_vars(s.sentence, to_jsonb(l)) new_sentence
from sentences s
left join logs l on l.sentenceid = s.id;
Upvotes: 2