Creating SQL Query references a few tables - sql

I tried to do this myself with views but I unfortunately I don't have the skills or knowledge required.
I've put in some dummy data to demonstrate.
uri of course = object_uri
TABLE_RECORD
+-----+---------------+---------+
| uri | title | client |
+-----+---------------+---------+
| 1 | australia | peter |
| 2 | new zealand | peter |
| 3 | canada | chris |
| 4 | united states | mitch |
| 5 | ireland | michael |
| 6 | scotland | mitch |
+-----+---------------+---------+
TABLE_UDF
+------------+--------------+----------------+
| object_uri | udf_type_uri | udf_type_value |
+------------+--------------+----------------+
| 1 | 2005 | 1/12/2007 |
| 2 | 2005 | 2/04/2008 |
| 2 | 2006 | 3/04/2009 |
| 3 | 2005 | 4/05/2010 |
| 4 | 2006 | 12/04/2016 |
| 5 | 2005 | 14/05/2005 |
| 5 | 2006 | 14/05/2006 |
| 6 | 2005 | 20/01/2017 |
+------------+--------------+----------------+
EXPECTED OUTPUT
+-----+---------------+---------+------------+------------+
| uri | title | client | udf_type_1 | udf_type_2 |
+-----+---------------+---------+------------+------------+
| 1 | australia | peter | 1/12/2007 | |
| 2 | new zealand | peter | 2/04/2008 | 3/04/2009 |
| 3 | canada | chris | 4/05/2010 | |
| 4 | united states | mitch | | 12/04/2016 |
| 5 | ireland | michael | 14/05/2005 | 14/05/2006 |
| 6 | scotland | mitch | 20/01/2017 | |
+-----+---------------+---------+------------+------------+
Thanks heaps in advanced.

If I understand correctly, this is a join with conditional aggregation:
select r.uri, r.title, r.client,
max(case when u.udf_type_uri = 2005 then udf_type_value end) as udf_type_1,
max(case when u.udf_type_uri = 2006 then udf_type_value end) as udf_type_2
from record r join
udf u
on r.uri = u.object_uri
group by r.uri, r.title, r.client;

SELECT
*
FROM
table_record
LEFT JOIN
(
SELECT
object_uri,
MAX(CASE WHEN udf_type_uri = 2005 THEN udf_type_value END) AS udf_type_1,
MAX(CASE WHEN udf_type_uri = 2006 THEN udf_type_value END) AS udf_type_2
FROM
table_udf
GROUP BY
object_uri
)
table_udf
ON table_udf.object_uri = table_record.uri
The inner query squashes the udf table down to one row per uri, and uses MAX() and CASE to ensure the correct udf is placed in the correct column. Then you just join the tables as normal.
(Can also be done using PIVOT, but that's always seems clunkier to me...)
https://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx

Related

match TableA.Id with TableB.Id Than return TableB.Country

I have two tables in my SQLite databaselike this
Table A:
| Id | Date | Rate | Person
|:-- |:-------:| ----:| ------:|
| 1 | 2022-02 | 6.3 | Alex |
| 1 | 2022-05 | 4.2 | John |
| 2 | 2022-09 | 2.5 | Alex |
| 3 | 2022-01 | 7.8 | David |
| 2 | 2022-21 | 9 | William|
Table B:
| Id | City | Country |
|:-- |:---------:| -------:|
| 1 | London | England |
| 2 | Paris | France |
| 3 | Washington| USA |
| 4 | Berlin | Germany |
I need a query to get Id and Rate of each row, in table A then get Country of that Id in Table B
The result should be something like this
Table C:
| Ids | Countries | Rates |
|:--- |:---------:| -----:|
| 1 | England | 6.3 |
| 1 | England | 4.2 |
| 2 | France | 2.5 |
| 3 | USA | 7.8 |
| 2 | France | 9 |
You need to join A and B with Id, and select some columns:
select B.Id as Ids,
B.Country as Countries,
A.Rate as Rates
From A inner join B on(A.Id = B.Id)

SQL JOIN each id in JSON object

I have a JSON column containing col_values for another table. I want to return rows from that other table for each item in the JSON object.
If this was an INT column, I would use JOIN, but I need to JOIN every entry in the JSON object.
Take:
writers :
| id | name | projects (JSON) |
|:-- |:-----|:------------------|
| 1 | Andy | ["1","2","3","4"] |
| 2 | Hank | ["3","4","5","6"] |
| 3 | Alex | ["1","7","8","9"] |
| 4 | Joe | ["1","5","6","7"] |
| 5 | Ken | ["2","4","5","6"] |
| 6 | Zach | ["2","7","8","9"] |
| 7 | Walt | ["2","5","6","7"] |
| 8 | Mike | ["2","3","4","5"] |
cities :
| id | name | project |
|:-- |:---------|:--------|
| 1 | Boston | 1 |
| 2 | Chicago | 2 |
| 3 | Cisco | 3 |
| 4 | Seattle | 4 |
| 5 | North | 5 |
| 6 | West | 6 |
| 7 | Miami | 7 |
| 8 | York | 8 |
| 9 | Tainan | 9 |
| 10 | Seoul | 1 |
| 11 | South | 2 |
| 12 | Tokyo | 3 |
| 13 | Carlisle | 4 |
| 14 | Fugging | 5 |
| 15 | Turkey | 6 |
| 16 | Paris | 7 |
| 17 | Midguard | 8 |
| 18 | Fugging | 9 |
| 19 | Madrid | 1 |
| 20 | Salvador | 2 |
| 21 | Everett | 3 |
I need every city ordered by name for Mike (id=8).
Desired results:
This is what I'm getting and what I need to get (ORDER BY name).
Output :
| id | name | project |
|:---|:---------|:--------|
| 13 | Carlisle | 4 |
| 2 | Chicago | 2 |
| 3 | Cisco | 3 |
| 21 | Everett | 3 |
| 14 | Fugging | 5 |
| 5 | North | 5 |
| 20 | Salvador | 2 |
| 4 | Seattle | 4 |
| 11 | South | 2 |
| 12 | Tokyo | 3 |
Current query, but this can't be the best way...
SQL >
SELECT c.*
FROM cities c
WHERE EXISTS (
SELECT 1
FROM writers w
WHERE JSON_CONTAINS(
w.projects, CONCAT('\"', c.project, '\"'))
AND w.id = '8'
)
ORDER BY c.name;
DB Fiddle with the above. Is there a better way to do this "properly"?
Background
If it matters, I need to keep using JSON as the datatype because my server-side software that uses this database normally reads that column best if presented as a JSON object.
I would normally just do several database calls and iterate through that JSON object in my server-side language, but that is way too expensive with so many database calls, notwithstanding that it is even more costly to do multiple database calls for pagination.
I need all the results in a single database call. So, I need to JOIN or otherwise loop through each item in the JSON object within SQL.
Start with JOIN
Per a comment from a user, there is a better way...
SQL >
SELECT c.*
FROM writers w
JOIN cities c ON JSON_CONTAINS(w.projects, CONCAT('\"', c.project, '\"'))
WHERE w.id = '8'
ORDER BY c.name;
Output is the same...
Output :
id
name
project
13
Carlisle
4
2
Chicago
2
3
Cisco
3
21
Everett
3
14
Fugging
5
5
North
5
20
Salvador
2
4
Seattle
4
11
South
2
12
Tokyo
3
DB Fiddle

using group by clause in a table with rank function implemented on it in Postgresql

How can we use the GROUP BY clause in a table with rank function implemented on it in Postgresql?
candidate_id | name | status | phone_num | process_fk
--------------+---------+--------------+-----------+------------
1 | james | regular | 0985905 | 1
2 | kylie | probationary | 098889487 | 2
3 | Anne | regular | 095590 | 1
4 | Olivia | probationary | 09556451 | 3
5 | Charles | regular | 09445099 | 2
6 | Hannah | regular | 0977988 | 2

SQL - Filter rows by attributes

I have 3 tables USERS, WORKFLOWS, ATTRIBUTES. WORKFLOWS tables values will be NEW, UPDATE and DELETE and ATTRIBUTES table contains workflow attributes like comment, doneBy, dateOfDone etc there are many. Users and Workflows table are connected by USR_ID and workflows and Attributes are connected by WF_ID.
+--------+------------+------------+ +--------+---------+------------+
|USR_ID | USR_NAME | IS_GENUINE | |WF_ID | WF_NAME | USR_ID |
+--------|------------+------------+ +--------|---------+------------+
| 1 | John | Y | | 1 | NEW | 1 |
| 2 | King | Y | | 2 | Update | 1 |
| 3 | Mark | N | | 3 | Delete | 1 |
| 4 | Smith | N | | 4 | Delete | 2 |
| 5 | Zack | Y | | 5 | Update | 2 |
+---------------------+------------+ | 6 | New | 3 |
| 7 | Update | 5 |
+------------------+------------+
+--------+--------------+----------------+-------+
|ATTR_ID | ATTR_NAME | ATTR_VALUE | WF_ID |
+--------|------------- +----------------+-------+
| 1 | comment | good | 1 |
| 2 | doneBy | suresh | 1 |
| 3 | comment | good | 2 |
| 4 | doneBy | suresh | 2 |
| 5 | comment | bad | 3 |
| 6 | doneBy | Raj | 3 |
| 7 | comment | Wow | 4 |
| 8 | doneBy | Amit | 4 |
| 9 | comment | good | 7 |
| 10 | doneBy | suresh | 7 |
+-----------------------+----------------+-------+
Now comes the major question I want to fetch the user info along with workflows and attributes if the IS_GENUINE = 'y' and comment = 'good' and doneBy = 'suresh'.
Expected Output
+--------+------------+---------+-----------+------------+
|USR_ID | USR_NAME | WF_NAME | ATTR_NAME | ATTR_VALUE |
+--------|------------+---------+-----------|------------+
| 1 | John | NEW | comment | good |
+--------|------------+---------+-----------|------------+
| 1 | John | NEW | doneBy | suresh |
+--------|------------+---------+-----------|------------+
| 1 | John | UPDATE | comment | good |
+--------|------------+---------+-----------|------------+
| 1 | John | UPDATE | doneBy | suresh |
+--------|------------+---------+-----------|------------+
| 1 | John | DELETE | comment | bad |
+--------|------------+---------+-----------|------------+
| 1 | John | DELETE | doneBy | Raj |
+--------|------------+---------+-----------|------------+
| 5 | Zack | UPDATE | comment | good |
+--------|------------+---------+-----------|------------+
| 5 | Zack | UPDATE | doneBy | suresh |
+--------|------------+---------+-----------|------------+
You can use windows function count as follows:
Select USR_ID, USR_NAME, WF_NAME, ATTR_NAME, ATTR_VALUE from
(Select U.USR_ID, U.USR_NAME, W.WF_NAME, A.ATTR_NAME, A.ATTR_VALUE,
count(case when a.attr_name = 'comment' and attr_value = 'good' then 1 end) over (partition by a.wf_id) as comm_cnt,
count(case when a.attr_name = 'doneBy' and attr_value = 'suresh' then 1 end) over (partition by a.wf_id) as suresh_cnt
from
Users u join workflow w
On u.user_id = w.user_id
Join attributes a on a.wf_id = w.wf_id
Where is_genuine = 'Y')
Where comm_cnt = 1 and suresh_cnt = 1;
Try this one:
SELECT user_id, usr_name, wf_name, attr_name, attr_value
FROM users AS u JOIN attributes AS a ON u.user_id = a.user_id , workflows
JOIN attr_value ON b.wf_id = c.wf_id
WHERE is_genuine = 'y' AND
(attr_name = 'comment' AND attr_value = 'good') OR
(attr_name = 'doneBy' AND attr_value = 'suresh');
P.S. I would restructure ATTRIBUTES. Try this:
+--------+---------+----------+-------+
|ATTR_ID | COMMENT | BY | WF_ID |
+--------|---------+----------+-------+
| 1 | good | suresh | 1 |
| 2 | good | suresh | 2 |
| 3 | bad | Raj | 3 |
| 4 | Wow | Amit | 4 |
| 5 | good | suresh | 7 |
+------------------+----------+-------+
Then the query can look like this:
SELECT user_id, usr_name, wf_name, attr_name, attr_value
FROM users AS u JOIN attributes AS a ON u.user_id = a.user_id , workflows
JOIN attr_value ON b.wf_id = c.wf_id
WHERE is_genuine = 'y' AND
(comment = 'good' AND by = 'suresh');
Results will look like this:
+---------+----------+---------+---------+--------+
| usr_id | usr_name | wf_name | comment | by |
+---------|----------+---------+---------|--------+
| 1 | John | new | good | suresh |
| 1 | John | update | good | suresh |
| 1 | John | delete | bad | Raj |
| 5 | Zack | update | good | suresh |
+---------|----------+---------+---------|--------+

SQL only select rows with max date within each user

SQL beginner here. I've got a simple test that users take, and each row is the answer to one of their questions. They're allowed to take the exam once per day, so some people take it a second time on another day, and thus will have many rows with different test dates. What I'm basically trying to do is get each user's most recent score.
Here is what my data looks like (table name is dumdum):
+----------+----------------+----------+------------------+
| USERNAME | CORRECT_ANSWER | RESPONSE | DATE_TAKEN |
+----------+----------------+----------+------------------+
| matt | 1 | 1 | 3/23/15 1:04:26 |
| matt | 2 | 2 | 3/23/15 1:04:28 |
| matt | 3 | 3 | 3/23/15 1:04:23 |
| david | 1 | 3 | 3/20/15 1:04:25 |
| david | 2 | 2 | 3/20/15 1:04:28 |
| david | 3 | 1 | 3/20/15 1:04:30 |
| david | 1 | 1 | 3/21/15 11:03:14 |
| david | 2 | 3 | 3/21/15 11:03:17 |
| david | 3 | 2 | 3/21/15 11:03:19 |
| chris | 1 | 2 | 3/17/15 12:45:52 |
| chris | 2 | 2 | 3/17/15 12:45:56 |
| chris | 3 | 3 | 3/17/15 12:45:59 |
| peter | 1 | 1 | 3/19/15 2:45:33 |
| peter | 2 | 3 | 3/19/15 2:45:35 |
| peter | 3 | 2 | 3/19/15 2:45:38 |
| peter | 1 | 1 | 3/20/15 12:32:04 |
| peter | 2 | 2 | 3/20/15 12:32:05 |
| peter | 3 | 3 | 3/20/15 12:32:05 |
+----------+----------------+----------+------------------+
and what I'm trying to get in the end...
+----------+------------------+-------+
| USERNAME | MOST_RECENT_TEST | SCORE |
+----------+------------------+-------+
| matt | 3/23/2015 | 100 |
| david | 3/21/2015 | 33 |
| chris | 3/17/2015 | 67 |
| peter | 3/20/2015 | 100 |
+----------+------------------+-------+
I ran into some trouble because I need to go by day, and not by day/time, so I had to do a weird maneuver where I went to character and back to date... This is what I have so far, but I can't figure out how to use only the scores from the most recent test (right now it's factoring in all scores from every test ever taken)...
SELECT username, to_date(substr(max(test_date),1,9),'dd-MON-yy') as most_recent_test, round((sum(case when response=correct_answer then 1 end)/3)*100,0) as score
FROM dumdum group by username
Any help would be appreciated! Thanks!
There are several solutions to this problem this one uses the WITH clause and the RANK function.
It also uses the TRUNC function rather than to_date(substr(
with mxDate as
(SELECT USERNAME,
TRUNC(DATE_TAKEN) as MOST_RECENT_TEST,
CASE WHEN CORRECT_ANSWER = RESPONSE THEN 1 else 0 END as SCORE,
RANK () OVER (PARTITION BY USERNAME
ORDER BY TRUNC(DATE_TAKEN) DESC) Rk
FROM dumdum)
SELECT
USERNAME,
MOST_RECENT_TEST,
SUM(SCORE)/3 * 100
FROM
mxDate
WHERE
rk = 1
GROUP BY
USERNAME,
MOST_RECENT_TEST
Demo