Add conditional statement in PostgreSQL query - postgresql

I have this query:
WITH grouped_messages AS
(SELECT
um.conversation_id,
array_agg(json_build_object('message', um.message, 'sent_at', um.created_at))
FROM user_messages um
INNER JOIN users_conversations c ON c.conversation_id = um.conversation_id
WHERE c.user_id = '#{user_id}' AND um.user_id != '#{user_id}' #{ "and um.created_at >= '#{timestamp}'" IF TIMESTAMP}
GROUP BY um.conversation_id
),
senders AS
(SELECT
gm.conversation_id,
json_build_object('id', u.id, 'user_name', u.user_name, 'avatar',
('https://my-staging.s3.amazonaws.com/public/uploads/user/' || u.id :: TEXT ||
'/avatar.jpg')) AS sender
FROM grouped_messages AS gm
INNER JOIN users_conversations c ON c.conversation_id = gm.conversation_id
INNER JOIN users u ON u.id = c.user_id
WHERE u.id != '#{user_id}'
)
SELECT json_object_agg(grouped_messages.conversation_id,
json_build_object('new_messages', grouped_messages.array_agg, 'sender', senders.sender))
FROM grouped_messages
INNER JOIN senders ON senders.conversation_id = grouped_messages.conversation_id
Which outputs, for example:
{
"62": {
"new_messages": [
{
"message": "some",
"sent_at": "2016-05-30T20:19:53.786024"
},
{
"message": null,
"sent_at": "2016-05-30T20:19:26.408814"
}
],
"sender": {
"id": "e4ba308b-a5cf-47ad-b8d6-d774eb325411",
"user_name": null,
"avatar": "https://my-staging.s3.amazonaws.com/public/uploads/user/e4ba308b-a5cf-47ad-b8d6-d774eb325411/avatar.jpg"
}
}
}
Now, what I need is to have a conditional statement in this query.
If u (user) avatar is not NULL then apply this 'https://my-staging.s3.amazonaws.com/public/uploads/user/' || u.id::text || '/avatar.jpg', otherwise just insert ':null' string (so that any JSON parser recognizes it as a null object).
Not sure how to put this if-else here in select statement and also how to ensure that part about JSON parser.

worked for me:
(SELECT gm.conversation_id, json_build_object('id', u.id, 'user_name', u.user_name,
'avatar', (
CASE WHEN u.avatar IS NULL THEN ':null'
ELSE ('https://my-staging.s3.amazonaws.com/public/uploads/user/' || u.id::text || '/avatar.jpg')
END))
You can provide case-statement in SELECT block as if it were value (with optional AS).

This is a good use of the CASE expression. In this scenario, you want to return either a hard URL to their avatar if the avatar field is not null, and NULL otherwise.
This is accomplished thus (omitting the other portions of the query for readability):
json_build_object('id', u.id, 'user_name', u.user_name,
'avatar',
CASE WHEN u.avatar IS NULL
THEN NULL
ELSE
(
'https://my-staging.s3.amazonaws.com/public/uploads/user/'
|| u.id :: TEXT ||
'/avatar.jpg') END) AS sender

Related

Complex "Order By" using Specification API Hibernate

Is there any way i can do this select query using Hibernate Specification API (criteria API)?
select * from employee order by (CASE
WHEN full_name IS NOT NULL
AND full_name <> '' THEN full_name
WHEN CONCAT(first_name, last_name) IS NOT NULL
AND CONCAT(first_name, last_name) <> '' THEN CONCAT(first_name, ' ', last_name)
END) nulls first
My specification method:
Specification { root: Root<Employee>, criteriaQuery: CriteriaQuery<*>, cb: CriteriaBuilder ->
...
criteriaQuery.orderBy(
cb.asc(
//TODO something?
)
)
...
}
Try this :
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Route> query = criteriaBuilder.createQuery(Employee.class);
List<Order> orders = new ArrayList();
Root<Employee> employeeRoot = query.from(Employee.class);
orders.add(criteriaBuilder.desc(employeeRoot .get("firstName")));
orders.add(criteriaBuilder.desc(employeeRoot .get("lastName")));
query.orderBy(orders);

EF Core Filtered Include with Select

Sorry for my English.
I ran into a mistake with EF Core 5.0.9 when is use Filtered Include and Select in same time and i don't know is a bug or feature. :)
return await _dbContext.User
.Where(u => !u.TOROLT)
.Where(u => ids.Contains(u.Id))
.Include(u => u.EventUsers.Where(eu => !eu.TOROLT && eu.EventId == eventId))
.Select(u => new UserDropDownDtoWithInviteData
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
EventUserId = u.EventUsers.First().Id,
IsCelebrated = u.EventUsers.First().IsCelebrated,
IsEventAdmin = u.EventUsers.First().IsEventAdmin,
IsInviteAccepted = u.EventUsers.First().IsInviteAccepted,
IsInvited = u.EventUsers.First().IsInvited,
})
.ToListAsync();
In this time the first elements is select is not from filtered include just from a normal include. SQL Script from Profiler:
SELECT
[u].[Id],
[u].[FirstName],
[u].[LastName],
(
SELECT TOP(1) [e].[Id]
FROM [dbo].[EventUser] AS [e]
WHERE [u].[Id] = [e].[UserId]) AS [EventUserId],
(
SELECT TOP(1) [e0].[IsCelebrated]
FROM [dbo].[EventUser] AS [e0]
WHERE [u].[Id] = [e0].[UserId]) AS [IsCelebrated],
(
SELECT TOP(1) [e1].[IsEventAdmin]
FROM [dbo].[EventUser] AS [e1]
WHERE [u].[Id] = [e1].[UserId]) AS [IsEventAdmin],
(
SELECT TOP(1) [e2].[IsInviteAccepted]
FROM [dbo].[EventUser] AS [e2]
WHERE [u].[Id] = [e2].[UserId]) AS [IsInviteAccepted],
(
SELECT TOP(1) [e3].[IsInvited]
FROM [dbo].[EventUser] AS [e3]
WHERE [u].[Id] = [e3].[UserId]) AS [IsInvited]
FROM [dbo].[User] AS [u]
WHERE ([u].[TOROLT] <> CAST(1 AS bit))
AND [u].[Id] IN (2, 1, 3, 4, 5)
But if is separate filtered include and select, than it work's fine, but this select complete record and not the part of him from database:
var a = await _dbContext.User
.Where(u => !u.TOROLT)
.Where(u => ids.Contains(u.Id))
.Include(u => u.EventUsers.Where(eu => !eu.TOROLT && eu.EventId == eventId))
.ToListAsync();
return a.Select(u => new UserDropDownDtoWithInviteData
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
EventUserId = u.EventUsers.First().Id,
IsCelebrated = u.EventUsers.First().IsCelebrated,
IsEventAdmin = u.EventUsers.First().IsEventAdmin,
IsInviteAccepted = u.EventUsers.First().IsInviteAccepted,
IsInvited = u.EventUsers.First().IsInvited,
})
.ToList();
Any idea why is this, and how can i solve solve this?
THX
Include is completely ignored if you have custom projection Select, so your filter will be also ignored. It is not a bug, Include works only when you get whole entity from query.
Anyway consider to rewrite your query:
var query =
from u in _dbContext.User
where !u.TOROLT && ids.Contains(u.Id)
from eu in u.EventUsers.Where(eu => !eu.TOROLT && eu.EventId == eventId)
.Take(1)
.DefaultIfEmpty()
select new UserDropDownDtoWithInviteData
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
EventUserId = eu.Id,
IsCelebrated = eu.IsCelebrated,
IsEventAdmin = eu.IsEventAdmin,
IsInviteAccepted = eu.IsInviteAccepted,
IsInvited = eu.IsInvited,
};
var result = await query.ToListAsync();

How how do I write a knex migration file to automatically update existing Database

I want to create a migration file for the following query
`insert into table1(column1)
select o.name from original_table as o`
`update original_table as o
set new_column = (select t1.id from table1 as t1
where t.column1 = o.old_column)`
So far I came up with this. The first part works but I am stuck with the second part
`exports.up = function (knex, Promise) {
return Promise.resolve()
.then(() => knex('original_table').select('old_column'))
.then((rows) => knex('table1').insert(rows))
.then(() => knex('table1 as t').select(['t.column1',
'column2']).join('original_table
as o', 'o.old_column', 't.column2'))
.then((rows) => knex('original_tableas
o').whereIn('original_table.old_column', rows.column2).update('column2',
rows.column1))
};
exports.down = function (knex) {
return Promise.resolve()
.then(() => console.log("Deletes updated records"));
};`
Thank you in advance.
First query is actually pretty inconvenient to form with knex, but knex.raw can do pretty much anything. Following would be the most knexy way I can think of to write it:
exports.up = async (knex) => {
// insert into table1(column1) select o.name from original_table as o
await knex.raw('insert into ?? (??) ??', ['table1', 'column1', knex('original_table').select('name')]);
// update original_table as o set
// new_column = (
// select t1.id from table1 as t1 where t1.column = o.old_column
// )
await knex('original_table as o').update({
new_column: knex('table1 as t1').select('id').where('t1.column', 'o.old_column')
});
}

IF and LEFT JOIN do not work

I have simple mysql query:
SELECT product.user_id, product.type, product.body, product.created, product.id, product.url, product.name, product.price, images.src
FROM xn_product AS product
LEFT JOIN xn_images AS images ON product.id = images.item_id
WHERE product.active = '1' AND images.type = 'product' AND product.promoted = '1' AND images.main = '1' AND product.url = '". $url "'
And I have problem. When in images.table dont't have product.id query is not executed. I tried to do something with IF and IF NULL but without success.
I want to see if it is empty images.src to overwrite them as 'empty.jpg' OR 1
Try using an INNER join
SELECT product.user_id, product.type, product.body, product.created, product.id, product.url, product.name, product.price, images.src
FROM xn_product AS product
INNER JOIN xn_images AS images ON product.id = images.item_id
WHERE product.active = '1' AND images.type = 'product' AND product.promoted = '1' AND images.main = '1' AND product.url = '". $url ."'

DBIx::Class query fails, yet dump from DBIC_TRACE works

I cannot rewrite the current DB schema right now BTW, but that's beside the point to the issue I've hit, so please ignore the table structure :D
I'm running this DB query:
my $rs = $dbx->resultset('Result')->search(
{
'result_hnd' => 16078055,
'seasons.outdoor' => 'venue.outdoors',
'seasons.start_date' => { '<=' => 'meet.date_end' },
'seasons.end_date' => { '>=' => 'meet.date_begin' },
},
{
'join' => [
{
'team' => {
'league_teams' => {
'league' => 'seasons',
},
},
},
{
'meet' => 'venue'
},
],
'+select' => ['seasons.season_hnd','seasons.name','seasons.start_date','seasons.end_date','meet.date_begin','meet.date_end'],
'+as' => ['season_hnd','season_name','s_start','s_end','m_start','m_end'],
columns => ['result_hnd'],
group_by => ['seasons.season_hnd'],
}
);
When I run this, I get no results. With DBIC_TRACE on, I see the generated SQL as:
SELECT me.result_hnd, seasons.season_hnd, seasons.name, seasons.start_date, seasons.end_date, meet.date_begin, meet.date_end FROM track.result me JOIN track.team team ON team.team_hnd = me.team_hnd LEFT JOIN track.league_team league_teams ON league_teams.team_hnd = team.team_hnd LEFT JOIN track.league league ON league.league_hnd = league_teams.league_hnd LEFT JOIN track.season seasons ON seasons.league_hnd = league.league_hnd OR seasons.league_hnd = league.parent_league_hnd JOIN track.meet meet ON meet.meet_hnd = me.meet_hnd JOIN track.venue venue ON venue.venue_hnd = meet.venue_hnd WHERE ( ( result_hnd = ? AND seasons.end_date >= ? AND seasons.outdoor = ? AND seasons.start_date <= ? ) ) GROUP BY seasons.season_hnd: '16078055', 'meet.date_begin', 'venue.outdoors', 'meet.date_end'
When I copy and paste this statement into my MYSQL client (and interpolate the placeholders), like this:
SELECT me.result_hnd, seasons.season_hnd, seasons.name, seasons.start_date, seasons.end_date, meet.date_begin, meet.date_end
FROM track.result me
JOIN track.team team ON team.team_hnd = me.team_hnd
LEFT JOIN track.league_team league_teams ON league_teams.team_hnd = team.team_hnd
LEFT JOIN track.league league ON league.league_hnd = league_teams.league_hnd
LEFT JOIN track.season seasons ON seasons.league_hnd = league.league_hnd OR seasons.league_hnd = league.parent_league_hnd
JOIN track.meet meet ON meet.meet_hnd = me.meet_hnd
JOIN track.venue venue ON venue.venue_hnd = meet.venue_hnd
WHERE ( ( result_hnd = 16078055 AND seasons.end_date >= meet.date_begin AND seasons.outdoor = venue.outdoors AND seasons.start_date <= meet.date_end ) )
GROUP BY season_hnd;
I get the exact result I expect (7 records).
This is really bizarre. To all intents and purposes, isn't that exactly the same query? Am I missing something in my debugging? Or is something else happening at the DBIx::Class::ResultSet layer that isn't being dumped?
To tell SQL::Abstract that the value on the right is actually an identifier, you can do the following (as outlined in the docs):
{
'result_hnd' => 16078055,
'seasons.outdoor' => { -ident => 'venue.outdoors' },
'seasons.start_date' => { '<=' => { -ident => 'meet.date_end' } },
'seasons.end_date' => { '>=' => { -ident => 'meet.date_begin' } },
},
To all intents and purposes, isn't that exactly the same query?
(For all intents and purposes, ...)
No. In the log, you have something equivalent to
result_hnd = '16078055'
AND seasons.end_date >= 'meet.date_begin'
AND seasons.outdoor = 'venue.outdoors'
AND seasons.start_date <= 'meet.date_end'
or maybe
result_hnd = 16078055
AND seasons.end_date >= 'meet.date_begin'
AND seasons.outdoor = 'venue.outdoors'
AND seasons.start_date <= 'meet.date_end'
In your attempt, you used
result_hnd = 16078055
AND seasons.end_date >= meet.date_begin
AND seasons.outdoor = venue.outdoors
AND seasons.start_date <= meet.date_end
Sorry, I don't have the solution for you.