SQL conditional join with default values - sql

I'm kinda struggling with this query, I have the following table:
setting
-------
id | name | value | type
--------------------------------
1 | title | Hi | string
2 | color | #ff0000 | string
user_setting
-------
id | userId | settingId | value
--------------------------------
1 | 1 | 1 | Hello
user
-------
id | email
1 | foo#test.com
I want to run a query that will select all settings for user 1, but also include the default value, so ideally I get this:
id | default | value
-----------------------
title | Hi | Hello
color | #ff0000 | null
My current query is
SELECT setting.id, setting.name, setting.value, user_setting.value, user.id
FROM setting
RIGHT JOIN user_setting
ON setting.id = "user_setting"."settingId"
LEFT OUTER JOIN user
ON "user_setting"."userId" = user.id
WHERE user.id = 1
But this only gives me the values that the user has defined.
EDIT: Updated setting table

I think you want a left join. But I think your setting is missing a column for setting_id (or whatever it is called). So the table should really be:
setting
-------
id | name | value | type
--------------------------------
1 | title | Hi | string
2 | color | #ff0000 | string
Otherwise user_settings.setting_id doesn't refer to anything. With this column, you want:
select s.name, s.value as default, us.value
from setting s left join
user_setting us
on us.setting_id = s.id and us.user_id = 1

Related

Postgresql remove duplicates from table in process of retrieval based on criteria

I have a business logic where I want to retrieve translations from the database, The translations can be overridden so overridden translations should be retrieved when available.
Schema:
i18n
-----
id
slug // unique
i18nTranslations
--------------
id
i18nId // referencing i18n.id
langId
text
overriddenType // pageOverride / instanceOverride
i18nPageOverrides
-----------------
id
translationId // referencing i18nTranslations.id
instanceId
pageId
Example:
i18nTranslations
------------------------------------------------------
id i18nId langId text type overrideType
------------------------------------------------------
1 | ABC | En | AAX | static |
2 | ABC | En | AAX Ovd | static | pageOverride
3 | ABC | Tr | TDF | static |
i18nPageOverride
--------------------------
transId pageId instanceId
--------------------------
2 login admin
Expected Output:
------------------------------------------------------
id i18nId langId text type overrideType
------------------------------------------------------
2 | ABC | En | AAX Ovd | static | pageOverride // overridden data
3 | ABC | Tr | TDF | static |
In the expected output above, The row with "AAX" text has been eliminated since it had overridden row for the lang.
Is there any way to achieve this behavior just by using a query?
A DISTINCT ON expression with an ORDER BY could be perfect for this.
The sorting can be on a descending i18nPageOverrides.id, with the nulls sorted last.
DISTINCT ON ( expression [, ...] ) keeps only the first row of each
set of rows where the given expressions evaluate to equal. The
DISTINCT ON expressions are interpreted using the same rules as for
ORDER BY (see above).
SELECT DISTINCT ON (tr.i18nId, tr.langId)
tr.id, tr.i18nId, tr.langId, tr.itText, tr.itType, tr.overrideType
FROM i18nTranslations tr
LEFT JOIN i18nPageOverrides po ON po.translationId = tr.id
ORDER BY tr.i18nId, tr.langId, po.id DESC NULLS LAST, tr.id;
id
i18nid
langid
ittext
ittype
overridetype
2
ABC
En
AAX Ovd
static
pageOverride
3
ABC
Tr
TDF
static
null
Test on db<>fiddle here
You can use ROW_NUMBER window function PARTITION BY and ORDER BY to make a row number for duplicate number then filter rn = 1 rows.
Query 1:
SELECT "iId",
"itI18nId",
"itLangId",
"itText",
"itType",
"itOverrideType"
FROM (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY "itI18nId","itLangId" ORDER BY "itOverrideType","iCreatedAt") rn
FROM i18n i
INNER JOIN i18n_translations t
ON i."iId" = t."itI18nId"
LEFT JOIN i18n_page_override o
ON o."ipoTranslationId" = t."itId"
) t1
WHERE rn = 1
Results:
| iId | itI18nId | itLangId | itText | itType | itOverrideType |
|--------------------------------------|--------------------------------------|--------------------------------------|--------|--------------|----------------|
| b76481bc-1171-4fb3-8433-31302ae39a81 | b76481bc-1171-4fb3-8433-31302ae39a81 | 175376f6-9dc8-4bea-bbc0-bf93744999c9 | Adi | staticNormal | (null) |
| b76481bc-1171-4fb3-8433-31302ae39a81 | b76481bc-1171-4fb3-8433-31302ae39a81 | 875dbdbb-9cb2-4f1b-a8ca-096321a0cd36 | Fn Ovd | staticNormal | stPageOverride |

SQL, BigQuery - completing missing values with other part of rows

I'm using Firebase data exported to BigQuery (data contains events data coming from mobile application). I've made an update to the application and new parameter is being reported. Unfortunately, not all users have the latest version of app. This is why I have rows with that parameter as well as rows without it.
In event_params I have something like:
| No | contentId | contentName |
|----|-----------|---------------------|
| 1 | abc | (parameter missing) |
| 2 | abc | Name of ABC |
| 3 | cde | Name of CDE |
| 4 | efg | Name of EFG |
| 5 | abc | (parameter missing) |
| 6 | cde | Name of CDE |
Now, when I query that table and I specify (using UNNEST) that I need contentName parameter, I don't get rows where that parameter is missing.
I have query:
SELECT
ep.value.string_value as ContentID,
ep2.value.string_value as ContentName,
COUNT(1) as `Count`
FROM
`mydataset.mytable.events_*`,
UNNEST(event_params) as ep,
UNNEST(event_params) as ep2
WHERE
event_name="my_event_name" AND
ep.key="contentID" AND
ep2.key="contentName"
GROUP BY 1,2
and I get:
| No | contentId | contentName | Count |
|----|-----------|-------------|-------|
| 1 | abc | Name of ABC | 1 |
| 2 | cde | Name of CDE | 2 |
| 3 | efg | Name of EFG | 1 |
However, I would like to get:
| No | contentId | contentName | Count |
|----|-----------|-------------|-------|
| 1 | abc | Name of ABC | 3 |
| 2 | cde | Name of CDE | 2 |
| 3 | efg | Name of EFG | 1 |
I want to complete somehow rows with missing contentName parameters using values from other rows with the same contentId (we can assume that each contentId has the same, constant contentName)
How can I achieve it? I thougt about SELF JOIN, but it's rather not recommended by BigQuery.
The solution provided by Gordon can be slightly modified in order to achieve what you intend:
SELECT contentId.value.string_value as ContentID,
MAX(contentName.value.string_value) as ContentName,
COUNT(1) as `Count`
FROM `mydataset.mytable.events_*` e LEFT JOIN
UNNEST(e.event_params) as contentId
ON contentId.key = 'contentID' LEFT JOIN
UNNEST(e.event_params) contentName
ON contentName.key = 'contentName'
WHERE e.event_name = 'my_event_name'
GROUP BY 1;
Note that I am grouping only by the ContentID and I am aggregating the ContentNames using MAX, which ignores null values.
I have recreated your example table and it works as expected.
You can update the table so that you fill the nulls and then make your query
[1]
UPDATE `your_project.your_dataset.your_table` t_incomplete
SET t_incomplete.contentName = t_complete.contentName
FROM `your_project.your_dataset.your_table` t_complete
WHERE t_incomplete.contentId = t_complete.contentId
AND t_complete.contentName IS NOT NULL
I am not sure how will this work with nested tables but you can always
UPDATE UNNESTING
UPDATE WITH QUERY [1]
UPDATE NESTING
You can picture the idea behind with this sample CREATE TABLE
CREATE TABLE `your_project.your_dataset.sample_table`
(
id INT64,
nullable STRING
);
INSERT INTO `your_project.your_dataset.sample_table`
VALUES (1, 'foo');
INSERT INTO `your_project.your_dataset.sample_table`
VALUES (1, null);
INSERT INTO `your_project.your_dataset.sample_table`
VALUES (2, 'lel');
INSERT INTO `your_project.your_dataset.sample_table`
VALUES (1, null);
INSERT INTO `your_project.your_dataset.sample_table`
VALUES (2, null);
and QUERY[2]
UPDATE `your_project.your_dataset.sample_table` t_incomplete
SET t_incomplete.nullable = t_complete.nullable
FROM `wave27-sellbytel-aalbesa.trial_dataset.with_and_update` t_complete
WHERE t_incomplete.id = t_complete.id
AND t_complete.nullable IS NOT NULL
This way you actually give the corresponding value to the cell and you can run your query without worries. I hope this works!
Do you just need an OR condition?
WHERE event_name = 'my_event_name' AND
ep.key = 'contentID' AND
(ep2.key = 'contentName' OR ep2.key IS NULL)
EDIT:
I think you need LEFT JOINs:
SELECT contentId.value.string_value as ContentID,
contentName.value.string_value as ContentName,
COUNT(1) as `Count`
FROM `mydataset.mytable.events_*` e LEFT JOIN
UNNEST(e.event_params) as contentId
ON contentId.key = 'contentID' LEFT JOIN
UNNEST(e.event_params) contentName
ON contentName.key = 'contentName'
WHERE e.event_name = 'my_event_name'
GROUP BY 1, 2;
Note: This should preserve the counts you want but might result in extra rows in the result set.

SQL Query manipulation

I have three tables :
BookingNode , Booking AirTrip
AirTrip :
+----+------------+
| ID | Name |
+----+------------+
| 0 | One way |
| 1 | Round trip |
| 2 | Circle |
| 3 | Other |
+----+------------+
When ever we make a booking we store the data as :
BookingNode table
+--------+-------------------+------------+----------------------+
| ID | CustomerGivenName | IPAddress | Email |
+--------+-------------------+------------+----------------------+
| 177022 | xfghfh | 2130706473 | mikehussey#gmail.com |
| 177021 | cfggjfj | 2130706473 | mikehussey#gmail.com |
+--------+-------------------+------------+----------------------+
Booking Table :
+--------+---------------+-----------+------------+------------+
| ID | BookingNodeID | AirTripID | AirLineId | Provider |
+--------+---------------+-----------+------------+------------+
| 181251 | 177020 | 1 | 978 | Jet |
| 181252 | 177021 | 0 | 982 | Go |
| 181253 | 177021 | 0 | 978 | Jet |
+--------+---------------+-----------+------------+------------+
If round trip flight is booked and ProviderID is same then a single entry is done in Booking Table with AirTripID value as 1.(Booking ID : 181251 and Provider Jet )
But if providers are different for both the legs then two entries are done in Booking Table with AirTripID for both entries are one(Booking ID : 181252 and 181253 Provider Go,Jet ).In this case BookingNodeID value being same.
Prob : I have to write a query to get different type of Bookings.(Oneway, RoundTrip,Circle).But when I apply join on AirTripID , it is giving me incorrect results.
How can I write my query to give correct results knowing that BookingNodeID is going to be the same for roundtrip (both entries in Booking Table)
Sample Output
+-------------+---------------+-------------------+------------+
| AirTripName | BookingNodeID | CustomerGivenName | IPAddress |
+-------------+---------------+-------------------+------------+
| TwoWay | 177020 | xfghfh | 2130706473 |
| TwoWay | 177021 | cfggjfj | 2130706473 |
+-------------+---------------+-------------------+------------+
Basically, this code might have an error due to my laziness syntom of data entry. But, the logic of the query is, if b.AirTripID is 0, add extra condition which group by Booking. if result return more than 1 row, is actually 2 way. so AirTripType will become 1, otherwise, remain the same as b.AirTripID. You may copy below on and try fix if theres any error. i believe the logic should work based on your expected result.
select
bd.ID,
bd.CustomerGivenName,
case b.AirTripID
when 1 then 1
when 2 then 2
when 3 then 3
when 0 then
case select BookingNodeID
from Booking
where Booking.BookingNodeID = bd.ID group by BookingNodeID having Count(BookingNodeID)
when 1 then 1
else 0 end as AirTripType,
bd.IPAddress
from BookingNode bd
inner join (select BookingNodeID ,AirTripID from Booking group by BookingNodeID ,AirTripID) as b on b.BookingNodeID = bd.ID
where id=177021
Try This
WITH CTE
AS
(
SELECT
SeqNo = ROW_NUMBER() OVER(PARTITION BY BN.ID ORDER BY B.ID),
B.BookingNodeID,
BN.CustomerGivenName,
BN.IPAddress,
AirTripId = A.ID,
AirTripNm = A.Name
FROM Booking B
INNER JOIN AirTrip A
ON A.ID = B.AirTripID
LEFT JOIN BookingNode BN
ON B.BookingNodeID = BN.id
)
SELECT
C1.SeqNo,
AirTripName = CASE WHEN C2.SeqNo IS NOT NULL
THEN 'Round trip'
ELSE C1.AirTripNm END,
C1.BookingNodeID,
C1.CustomerGivenName,
C1.IPAddress
FROM CTE C1
LEFT JOIN CTE C2
ON C1.BookingNodeID = C2.BookingNodeID
AND C2.SeqNo = 2
WHERE c1.SeqNo = 1
SQL Fiddle Link Here
Select distinct bk.bookingnodeid,cst.customername,ipaddress,
case when count(airtripid)over(partition by bookingnodeid order by bookingnodeid)=2 then 'RoundTrip' else name end As AirTripName
from booking bk
inner join airlinetrip at
on bk.airtripid=at.id
inner join customer cst
on cst.id=bk.bookingnodeid

HQL join query to join to a single row of many to one relationship

This HQL query has been driving me up a wall, and I hope someone can help me out. Here is my data model:
public class Record {
private int id;
private String name;
private Set<RecordFieldData> recordFieldData;
}
public class RecordFieldData {
private int id;
private String data;
private Record record;
private RecordTypeField type;
}
public class RecordTypeField {
private int id;
private String dataType;
}
Here is some data:
Record
-------------------------------------------------
| id | name |
-------------------------------------------------
| 1 | Abc |
| 2 | 123 |
| 3 | Xyz |
-------------------------------------------------
RecordFieldData
-------------------------------------------------
| id | record_id | data | type_id |
-------------------------------------------------
| 1 | 1 | Blue | 1 |
| 2 | 1 | Round | 2 |
| 3 | 2 | Red | 1 |
| 4 | 2 | Square | 2 |
| 5 | 3 | White | 1 |
| 6 | 3 | Oval | 2 |
-------------------------------------------------
RecordTypeField
-------------------------------------------------
| id | dataType |
-------------------------------------------------
| 1 | Color |
| 2 | Shape |
-------------------------------------------------
What I need is a list of Records that are sorted by RecordField.data of a certain type. For example, sort the Records on RecordField.data but only for RecordFieldData of type 'color'. RecordFieldData.data does not have to be returned in the query, I can get that later, but I need the sort to happen in the query that retrieves the records (otherwise pagination won't work). Keep in mind RecordFieldData of a certain type can be missing for a Record but I still want the record in the list.
I tried this query but I am getting duplicate records because it is joining RecordFieldData rows that I do not want:
SELECT r FROM Record r
LEFT JOIN r.recordFieldData AS field
LEFT JOIN field.type AS typeField WITH typeField.dataType = 'color'
ORDER BY LOWER(field.data)
Any suggestions?
EDIT
Just saw your requirement of needing to return all records. So replacing LEFT JOIN with JOIN as I initially suggested won't work.
Try using DISTINCT instead
SELECT DISTINCT r FROM Record r
LEFT JOIN r.recordFieldData AS field
LEFT JOIN field.type AS typeField WITH typeField.dataType = 'color'
ORDER BY LOWER(field.data)
EDIT 2
I think LEFT JOIN FETCH needs to be used, though I'm not sure why it gave you an error the last time. Maybe something like this
SELECT DISTINCT r FROM Record r
LEFT JOIN FETCH r.recordFieldData AS field
LEFT JOIN FETCH field.type AS typeField WITH typeField.dataType = 'color'
ORDER BY LOWER(field.data)

Codeigniter - get related entries

I have two tables, one that store some data like name, sex etc. end the seconds that store images related from the id of the first table.
I need to get the row of the first table and get also all the images from the second one and I would like to get them with a single query.
How can I do that? is it possibile?
I have tried with a simple join but I get just the first element of the second table.
Example Table 1:
----------------------
|ID | Name | Sex |
----------------------
| 1 | Chris | Male |
----------------------
| 2 | Elisa | Female |
----------------------
Table 2:
------------------------------
| ID | User_id | Image name |
------------------------------
| 1 | 1 | img1.jpg |
------------------------------
| 2 | 1 | img2.jpg |
------------------------------
| 3 | 1 | img3.jpg |
------------------------------
| 4 | 2 | img4.jpg |
------------------------------
I would like to have:
Chris, Male, img1.jpg, img2.jpg, img3.jpg
Elisa, Female, img4.jpg
And so on...any advice?
SELECT
table1.*,
GROUP_CONCAT(table2.image_name SEPARATOR ', ') as image_name
FROM table1
LEFT JOIN table2
ON table1.id = table2.user_id
GROUP BY table1.id;
Demo : SQL Fiddle
Try with a left outer join
SELECT user.*, image.image_name as image
FROM user
LEFT OUTER JOIN image
ON user.id = image.user_id
It will get all the user rows and put empty val in image if none was found.