How can i query table within table? - mysql

How can i make a query that takes everything from one table then joins another table and put the values from the second table in a certain column in the result
What i am asking can be better explained:
clients:
id | name | age | ...
---------------------
15 | something | 30 |
17 | somethiaa | 30 |
13 | ggggthing | 30 |
clients_meta:
id | client_id | meta_key | meta_value |
-----------------------------------------
1 | 15 | location | NY |
2 | 15 | height | 195 |
3 | 15 | job | student |
4 | 13 | location | TN |
This is my current query:
SELECT
`clients`.*,
`clients_meta`.*
FROM `clients`
JOIN clients_meta ON ( clients_meta.client_id = clients.id )
WHERE
`clients_age` = '30'
how can instead of a ugly table like that:
15 | something | 30 | 1 | 15 | location | NY |
15 | something | 30 | 2 | 15 | height | 195 |
15 | something | 30 | 3 | 15 | job | student |
change it to something like:
15 | something | 30 | 1 | 15 | location | NY |
| 2 | 15 | height | 195 |
| 3 | 15 | job | student |
thanks

You can use a variable to check whether the last id is equal to the current id, and in that case output null or '' instead.
select
case when c.ClientId <> #clientid then c.Name else '' end as ClientName,
case when c.ClientId <> #clientid then #ClientId := c.ClientId else '' end as ClientId,
p.ContactId,
p.Name as ContactName
from
Clients c
inner join Contacts p on p.ClientId = c.Clientid
, (select #clientid := -1) x
order by
c.ClientId, p.ContactId
Example: http://sqlfiddle.com/#!2/658e4c/6
Note, this is a bit hacky. I deliberately made ClientId the second field, so I could change and return the clientId variable in the same field. In other, more elaborate cases, you may have to do that in a separate field. But to eliminate that placeholder field from the result, you'll have to embed the whole query in a subselect, and define the wanted fields in the right order on the top level query.

You could choose one value in clients_meta.meta_key to always be first like 'location'. Then you can sort by clients.id, then by whether or not meta_key = 'location'. Any rows where meta_key != 'location', you can hide, like this:
select
case when clients_meta.meta_key = 'location'
then clients.id else '' end as id
, case when clients_meta.meta_key = 'location'
then clients.name else '' end as name
, case when clients_meta.meta_key = 'location'
then clients.age else '' end as age
, clients_meta.*
from clients join clients_meta on (clients_meta.client_id = clients.id)
where clients.age = '30'
order by clients.id, clients_meta.meta_key = 'location' desc;
You will obtain the results that you wanted:
+------+-----------+------+----+-----------+----------+------------+
| id | name | age | id | client_id | meta_key | meta_value |
+------+-----------+------+----+-----------+----------+------------+
| 13 | ggggthing | 30 | 4 | 13 | location | TN |
| 15 | something | 30 | 1 | 15 | location | NY |
| | | | 3 | 15 | job | student |
| | | | 2 | 15 | height | 195 |
+------+-----------+------+----+-----------+----------+------------+

Related

How Affect Group By to Other Second Join Table

I have some table like this
table request_buys
| id | invoice | user_id |
| -- | ----------------- | ------- |
| 3 | 20220405/01104298 | 1 |
table traces
| id | request_buy_id | status_id | created_at |
| -- | -------------- | --------- | ------------------- |
| 37 | 3 | 1 | 2022-03-27 14:12:25 |
| 38 | 3 | 2 | 2022-03-28 14:12:25 |
| 39 | 3 | 3 | 2022-03-29 14:12:25 |
| 40 | 3 | 4 | 2022-03-30 14:12:25 |
| 41 | 3 | 5 | 2022-03-31 14:12:25 |
| 42 | 3 | 6 | 2022-04-01 14:12:25 |
table statuses
| id | nama |
| -- | ----------------- |
| 1 | Order Placed |
| 2 | Order Paid |
| 3 | Accepted |
| 4 | Picked by Courier |
| 5 | In Transit |
| 6 | Delivered |
| 7 | Rated |
| 8 | Rejected |
| 9 | Canceled |
and then i try to design query like below
select
request_buys.invoice,
MAX(traces.id) as traces_id,
MAX(statuses.nama) as statuses_nama
from
`request_buys`
inner join `traces` on `request_buys`.`id` = `traces`.`request_buy_id`
inner join `statuses` on `traces`.`status_id` = `statuses`.`id`
where
`user_id` = 1
group by
request_buys.id
and produces output like the following
output
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Picked by Courier |
and the output i expect should be like in the table below
expect
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Delivered |
I understand my error is in MAX(statuses.nama) which I should change like removing MAX() in statuses.nama
But i just get error like this "SELECT list is not in GROUP BY clause and contains nonaggregated ... this is incompatible with sql_mode=only_full_group_by"
then I tried some to clear the value "ONLY_FULL_GROUP_BY" with a query like the following
SET sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY',''))
and the result is like this
output
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Order Placed |
and I'm really stuck at this
and how to make trace_id.status_id from the "GROUP BY" result based on request_buys.id still have a relationship with statuses.id
Your problem lies with your misuse of the MAX(statuses.nama) expression. Based on your expected output,you intend to get the statuses.nama which matches the MAX(traces.id), NOT the MAX(statuses.nama) value which returns the highest value in terms of alphabetic order. In this case, the initial letter 'P' > 'D' . I have tweaked your code a bit and tried it on workbench,supposing there are more than one invoice for a particular user.(e.g insert into request_buys values (4,'20230405/01104298',1); insert into traces values (43,4,7,'2022-04-01 14:12:25');) It works as intended.
select invoice, t.id as traces_id, s.nama as statuses_name from request_buys r
join traces t on r.id=t.request_buy_id
join statuses s on t.status_id=s.id
join
(select traces.request_buy_id, MAX(traces.id) as traces_id
from `request_buys`
inner join `traces` on `request_buys`.`id` = `traces`.`request_buy_id`
where
`user_id` = 1
group by
traces.request_buy_id ) join_t
on t.request_buy_id=join_t.request_buy_id and t.id=join_t.traces_id
;
If I'm understanding correctly, you're trying to retrieve the most recent status for each invoice. Using MAX(nama) won't return that result, because it just picks the maximum status name alphabetically.
Assuming you're using MySQL 8.x, use ROW_NUMBER() to sort and rank the statuses for each invoice, by the most recent date first. Then grab the latest one using where rowNum = 1
WITH cte AS (
SELECT rb.id AS request_buy_id
, rb.invoice
, t.id AS traces_id
, s.nama AS statuses_nama
, ROW_NUMBER() OVER(PARTITION BY rb.id ORDER BY t.created_at DESC) AS RowNum
FROM request_buys rb
INNER JOIN traces t ON rb.id = t.request_buy_id
INNER JOIN statuses s ON t.status_id = s.id
WHERE user_id = 1
)
SELECT *
FROM cte
WHERE RowNum = 1
;
Result:
request_buy_id
invoice
traces_id
statuses_nama
RowNum
3
20220405/01104298
42
Delivered
1
db<>fiddle here

How to select max value from rows and join to another table

I am trying to join two tables with respect to the max values for the values column. I would like to produce the expected results as shown below based on the max value while joining
select * from order
-------------------------
| ID | value | Name |
-------------------------
| 1 | 23 | REM |
| 2 | 0 | SER |
| 3 | 13 | MH |
| 4 | 3 | MH |
| 5 | 1 | MP |
-------------------------
select * from product
-------------------------
| ID | value | Name |
-------------------------
| 1 | 2 | ABC |
| 2 | 2 | DEG |
| 3 | 17 | XYZ |
-------------------------
Desired result:
-------------------------
| ID | Value | Name |
-------------------------
| 1 | 23 | REM |
| 2 | 2 | DEG |
| 3 | 17 | XYZ |
| 4 | 3 | MH |
| 5 | 1 | MP |
-------------------------
I have tried something like below but it's not fetching the value (NAME) from other table
SELECT
MAX(IF(a.value >b.value , a.value ,b.value )) AS Value
from order a left join product b on a.ID= b.ID
Please suggest how to get the expected result from these two tables.
Below is for BigQuery Standard SQL
#standardsql
select as value array_agg(struct(id, value, name) order by value desc limit 1)[offset(0)]
from
(
select * from `project.dataset.order`
union all
select * from `project.dataset.product`
)
group by id
with output
You can do this using a full join:
select id,
(case when p.val is null or p.val < o.val then o.val else p.val end),
(case when p.val is null or p.val < o.val then o.name else p.name end)
from product p full join
order o
using (id);
I just find this the simplest way to think about the problem.

Get values from 3 tables into a summarized table SQL

I've tried other solutions I've found here but I don't get the correct information back.
I have a table with many different records. A list of names with a status of active. Then I have another table which holds information for each name with a ticket number and then an assignment 'Assigned' and 'Feedback'. Not all names have a ticket.
Then 1 more table that holds a number of hours that goes towards that ticket number.
I want a summary of this information for each name whether there is info there or not. So I started with a subquery here is what I have.
select z.name as 'Name', round(coalesce(sum(x.Hours),0),2) as "Assigned",
round(coalesce(sum(y.Hours),0),2) as "Feedback" from
(select name from namelist where status = 'Active') as z
left join
(select e.realname as "Name", b.id as "Ticket", b.status as "Status", c.value -
COALESCE(sum(a.Hours),0) as "Hours" from user_table e
join ticket_table b ON b.handler_id = e.id
join custom_table c ON c.bug_id = b.id AND c.field_id = 7
left custom_table d ON d.bug_id = b.id AND d.field_id = 15
left hours_table a ON a.Ticket = b.id
where (b.status = 50)
Group By b.id
ORDER BY `Name` ASC, `Status` DESC) x on z.Name= x.Name
left JOIN
(select e.realname as "Name", b.id as "Ticket", b.status as "Status", c.value -
COALESCE(sum(a.Hours),0) as "Hours" from user_table e
join ticket_table b ON b.handler_id = e.id
join custom_table c ON c.bug_id = b.id AND c.field_id = 7
left custom_table d ON d.bug_id = b.id AND d.field_id = 15
left hours_table a ON a.Ticket = b.id
where (b.status = 20)
Group By b.id
ORDER BY `Name` ASC, `Status` DESC) y on z.Name= y.Name
Group by Name
I've changed some of the names around but this is the basic idea. b.status = 50 means Assigned, and 20 means Feedback. Those joins create a table that looks like this:
---------------------------------------------------------------------------------------------------
| Name | Ticket | Status| Hours ((value from custom_table)-(sum from hours table based on ticket))|
| Joe | 234 | 50 | 20 |
| Joe | 235 | 50 | 30 |
| Joe | 236 | 50 | 40 |
| John | 233 | 50 | 10 |
| John | 237 | 50 | 20 |
| John | 238 | 50 | 20 |
---------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
| Name | Ticket | Status| Hours ((value from custom_table)-(sum from hours table based on ticket))|
| Joe | 134 | 20 | 60 |
| Joe | 135 | 20 | 30 |
| Joe | 136 | 20 | 40 |
| John | 133 | 20 | 70 |
| John | 137 | 20 | 20 |
| John | 138 | 20 | 20 |
---------------------------------------------------------------------------------------------------
-----------------
| Name | Status |
| Joe | Active|
| John | Active|
| Mary | Active|
| Tom | Active|
| John |Inactive|
-----------------
Desired result:
----------------------------
| Name | Assigned| Feedback|
| Joe | 90 | 130 |
| John | 50 | 110 |
| Mary | 0 | 0 |
| Tom | 0 | 0 |
----------------------------
Now the Hours table is c.value which is a 1 to 1 relation subtract sum(hours) from hours table 1 to many relationship.
If I take out one of the joins, the table works. When I put them together like this, the numbers are incorrect. I can get the assigned correct if I only use that join. I can get the feedback numbers correct if I only use the feeback join. However it doesn't work when trying to get either from them. Let me know if you need more info I'll try my best to provide.
Example results:
----------------------------
| Name | Assigned| Feedback|
| Joe | 392 | 145 |
| John | 125 | 94 |
| Mary | 0 | 0 |
| Tom | 0 | 0 |
----------------------------
If I just use the table with status 50.
----------------------------
| Name | Assigned|
| Joe | 90 |
| John | 50 |
| Mary | 0 |
| Tom | 0 |
----------------------------
If I just use the table with status 20.
----------------------------
| Name | Assigned|
| Joe | 130 |
| John | 110 |
| Mary | 0 |
| Tom | 0 |
----------------------------
Don't worry about the custom tables so much, there is a reason they are there but aren't a part of my question. The biggest thing is simply getting the c.value from there, the other join to that table is only for another status, but not relevant to what I'm trying to accomplish.
The two Left Joins seem identical to me apart from the status (unless I am missing something)
Have you tried using a single left join and then an aggregation with a Case statement i.e.
SELECT Name,
SUM(CASE WHEN Status = 50 THEN Hours ELSE 0 END) AS Assigned,
SUM(CASE WHEN Status = 20 THEN Hours ELSE 0 END) AS Feedback
FROM table
GROUP BY Name
I appreciate the answer here is over-simplified but it's more a suggestions since I don't know the content of all the tables mentioned in the query
SQL FIDDLE Example (apologies using SQL SERVER but logic in MySQL is the same for this simple example)
http://sqlfiddle.com/#!18/f77e4/5/0
The output matches the desired results but the issue might lie with another table.
This assumes a single table but the logic should work the same using a left join

Find min value in column by grouping more than one column in sql

+----+-------+-----------------------------+-------------------------+-------------------+-------------+
| Id | grade | shot_name | submitted_by_supervisor | version_submitted | submit_type |
+----+-------+-----------------------------+-------------------------+-------------------+-------------+
| 27 | A | elx_reel01_scn1020_shot1720 | Salil Devji | 33 | Fresh |
| 27 | A | elx_reel01_scn1020_shot1720 | Deepali | 34 | Fresh |
| 37 | A | elx_reel01_scn1030_shot3480 | Salil Devji | 15 | Fresh |
| 37 | A | elx_reel01_scn1030_shot3480 | Salil Devji | 20 | Fresh |
| 7 | B | elx_reel01_scn1010_shot1030 | Darshan | 4 | Fresh |
| 7 | B | elx_reel01_scn1010_shot1030 | Varion | 6 | Fresh |
| 17 | B | elx_reel01_scn1010_shot1140 | Varion | 17 | Fresh |
| 17 | B | elx_reel01_scn1010_shot1140 | Varion | 14 | Fresh |
+----+-------+-----------------------------+-------------------------+-------------------+-------------+
I have column (submit_type) inserting new column in that value are set by CASE condition
here is my sql query :
SELECT s.shot_id,
s.shot_name ,
isn.reviewer AS 'submitted_by_supervisor',
isn.version AS 'version_submitted',
isn.grade AS 'grade',
CASE
WHEN isn.version = Min(isn.version) THEN 'FRESH'
ELSE 'Once Submitted'
end AS 'submit_type'
FROM viewd_elx.india_supe_note isn
JOIN viewd_elx.shot s
ON s.shot_id = isn.shot_id
JOIN viewd_elx.team t
ON isn.shot_id = t.shot_id
JOIN viewd_elx.viewd_team vt
ON isn.shot_id = vt.shot_id
WHERE isn.promoted='Yes'
AND isn.grade IN ('A',
'B')
GROUP BY isn.grade,
s.shot_id,
isn.version;
mentioned query gives 'Fresh' value in all field in (submit_type) column. which is not correct.
What I need exactly is, I am grouping column and finding MIN(value) of column,
Work Flow: -
grade A => group (shot_name) => min(version_submited) => set 'Fresh' else 'Once submitted' in (submit_type)
In Grade 'A' have same shot_name, grouping this (shot_name) and then finding min(version_submited) if min value found then set 'fresh' else set 'once submitted' value in (submit_type) column.
Also for Grade B like Grade A.
I need result like this =>
+----+-------+-----------------------------+-------------------------+-------------------+----------------+
| Id | grade | shot_name | submitted_by_supervisor | version_submitted | submit_type |
+----+-------+-----------------------------+-------------------------+-------------------+----------------+
| 27 | A | elx_reel01_scn1020_shot1720 | Salil Devji | 33 | Fresh |
| 27 | A | elx_reel01_scn1020_shot1720 | Deepali | 34 | Once Submitted |
| 37 | A | elx_reel01_scn1030_shot3480 | Salil Devji | 15 | Fresh |
| 37 | A | elx_reel01_scn1030_shot3480 | Salil Devji | 20 | Once Submitted |
| 7 | B | elx_reel01_scn1010_shot1030 | Darshan | 4 | Fresh |
| 7 | B | elx_reel01_scn1010_shot1030 | Varion | 6 | Once Submitted |
| 17 | B | elx_reel01_scn1010_shot1140 | Varion | 17 | Once Submitted |
| 17 | B | elx_reel01_scn1010_shot1140 | Varion | 14 | Fresh |
+----+-------+-----------------------------+-------------------------+-------------------+----------------+
The issue here is that you are grouping by on isn.version field as well, and trying to compute the minimum of the same field in that group. It will simply return the same value, and that is why all are coming as "Fresh". You will need to determine the minimum isn.version value for a group of (grade, shot_id) separately in a subquery (Derived Table), and then use that to check whether minimum or not.
SELECT s.shot_id,
s.shot_name ,
isn.reviewer AS submitted_by_supervisor,
isn.version AS version_submitted,
isn.grade AS grade,
CASE
WHEN isn.version = dt.min_version THEN 'FRESH'
ELSE 'Once Submitted'
END AS 'submit_type'
FROM viewd_elx.india_supe_note isn
JOIN viewd_elx.shot s
ON s.shot_id = isn.shot_id
JOIN viewd_elx.team t
ON isn.shot_id = t.shot_id
JOIN viewd_elx.viewd_team vt
ON isn.shot_id = vt.shot_id
JOIN (
SELECT grade,
shot_id,
MIN(version) AS min_version
FROM viewd_elx.india_supe_note
WHERE promoted = 'YES'
AND grade IN ('A', 'B')
GROUP BY grade,
shot_id
) AS dt
ON dt.grade = isn.grade
AND dt.shot_id = isn.shot_id
WHERE isn.promoted='Yes'
AND isn.grade IN ('A','B')
GROUP BY s.shot_id,
s.shot_name,
isn.reviewer,
isn.version,
isn.grade,
dt.min_version

MySQLl key-value store ordering with specific condition

I have the following structure:
+----------+--------+---------------------+
| id| gr_id| name | value |
+----------+--------+---------------------+
| 1 | 11 | name | Burro |
| 2 | 11 | submit | 2019/05/10 |
| 3 | 11 | date | 2019/05/17 |
| 4 | 12 | name | Ajax |
| 5 | 12 | submit | 2019/05/10 |
| 6 | 12 | date | 2019/05/18 |
+----------+--------+---------------------+
I have to order it by the date(if the name is date), from highest to lowest date, also it has to keep the groups (gr_id) without mixing the elments.
The desired result would look like this:
+----------+--------+---------------------+
| id| gr_id| name | value |
+----------+--------+---------------------+
| 4 | 12 | name | Ajax |
| 5 | 12 | submit | 2019/05/10 |
| 6 | 12 | date | 2019/05/18 |
| 1 | 11 | name | Burro |
| 2 | 11 | submit | 2019/05/10 |
| 3 | 11 | date | 2019/05/17 |
+----------+--------+---------------------+
How can i implement this?
You'll have to associate the group ordering criteria with all the elements of the group. You can do it through a subquery, or a join.
Subquery version:
SELECT t.*
FROM (SELECT gr_id, value as `date` FROM t WHERE `name` = 'date') AS grpOrder
INNER JOIN t ON grpOrder.gr_id = t.gr_id
ORDER BY grpOrder.`date`
, CASE `name`
WHEN 'name' THEN 1
WHEN 'submit' THEN 2
WHEN 'date' THEN 3
ELSE 4
END
Join version:
SELECT t1.*
FROM t AS t1
INNER JOIN AS t2 ON t1.gr_id = t2.gr_id AND t2.`name` = 'date'
ORDER BY t2.value
, CASE t1.`name`
WHEN 'name' THEN 1
WHEN 'submit' THEN 2
WHEN 'date' THEN 3
ELSE 4
END