Order MySQL data by row value - mysql

I have two tables that looks like this:
Table: items
id | itemId
---|------
0 | 1
1 | 2
2 | 3
Table: item_specs
id | itemId | key | values
---|--------|---------------
0 | 1 | itemreceived | 2012-06-01
1 | 1 | modelyear | 1992
2 | 1 | model | 2
3 | 2 | itemreceived | 2012-06-05
4 | 2 | modelyear | 2003
5 | 2 | model | 1
6 | 3 | itemreceived | 2012-07-05
7 | 3 | modelyear | 2000
8 | 3 | model | 3
My current query looks like this:
SELECT items.*, item_specs.* FROM item_specs
INNER JOIN item_specs ON items.itemId = item_specs.itemId
WHERE itemId IN(1,2,3)
How can I order the result by a key value, for example: model?
The result I'm looking for is something like this: (if I order by model)
id | itemId | key | values
---|--------|---------------
3 | 2 | itemreceived | 2012-06-05
4 | 2 | modelyear | 2003
5 | 2 | model | 1
0 | 1 | itemreceived | 2012-06-01
1 | 1 | modelyear | 1992
2 | 1 | model | 2
6 | 3 | itemreceived | 2012-07-05
7 | 3 | modelyear | 2000
8 | 3 | model | 3
The content that is returned is ordered by the value that is that has the key model

You need the model number for every row. You can do that with a join:
SELECT items.*, item_specs.*
FROM item_specs
INNER JOIN item_specs ON items.itemId = item_specs.itemId
INNER JOIN item_specs aux ON (aux.key = 'model' and aux.itemID = item_specs.itemId)
WHERE item_specs.itemId IN(1,2,3)
ORDER BY aux.values/*this is the model*/, item_specs.id;
or with a subselect:
SELECT items.*,
item_specs.*,
(select aux.values
from item_specs aux
where aux.key = 'model' and aux.itemID = item_specs.itemId
) as model
FROM item_specs
INNER JOIN item_specs ON items.itemId = item_specs.itemId
WHERE item_specs.itemId IN(1,2,3)
ORDER BY model, item_specs.id;

SELECT * FROM `table` WHERE `key` = 'model' ORDER BY `values` ASC
You have to manually specify a table type/storage engine. That can't be seen in the structure you provided.
Read more here.

It seems you want to use an order by clause. This will order by the columns you need. You can also do sneaky things here, like insert a true/false value for what you order by first.
SELECT * FROM `table`
Order by (case When Key='model' then 0 else 1 end), values
See, for instance, http://blog.sqlauthority.com/2007/07/17/sql-server-case-statement-in-order-by-clause-order-by-using-variable/

SELECT * FROM `table`
WHERE `key` = 'model'
ORDER BY `values`;

Related

SQL/MySQL - Select and return array column on one-to-many table join [duplicate]

We have 3 tables :
donations
purposes
expenses
Donations :
+--------+------+
| do_id | name |
+--------+------+
| 1 | A |
| 2 | B |
| 3 | A |
| 4 | D |
| 5 | B |
| 6 | B |
| 7 | A |
| 8 | B |
+--------+----- +
purposes:
+-------+-------+--------+
| pu_id | do_id | purpose|
+-------+-------+--------+
| 1 | 2 | abc |
| 2 | 2 | def |
| 3 | 2 | gih |
| 4 | 3 | jkl |
+-------+-------+--------+
expense :
+-------+-------+---------+
| ex_id | do_id | expense |
+-------+-------+---------+
| 1 | 2 | abc |
| 2 | 2 | def |
| 3 | 2 | gih |
| 4 | 3 | jkl |
+-------+-------+---------+
Now i want to make query to get all donations for donor B and join purposes table to get all purposes related to every donation_id then join expenses table to get all expenses related to donation_id and put all of that in every loop independently something like that
Row number 0
donation_id = 1
array(purposes)
array(expenses)
Row number 1
donation_id = 2
array(purposes)
array(expenses)
Row number 2
donation_id = 3
array(purposes)
array(expenses)
Row number 3
donation_id = 4
array(purposes)
array(expenses)
This is my try :
SELECT *, (
SELECT *
FROM `donation_purposes`
WHERE `donation_purposes`.`dopu_donation_id` = 4
) AS `purposes`
FROM `donations`
WHERE `donation_id` = '4'
thanks in advance
You should be able to solive this with an aggregate query using MySQL aggregate function JSON_ARRAYAGG(), like :
SELECT
d.do_id,
JSON_ARRAYAGG(p.purpose) purposes,
JSON_ARRAYAGG(e.expense) expenses
FROM donations d
INNER JOIN purposes p ON p.do_id = d.do_id
INNER JOIN expense e ON e.do_id = d.do_id
GROUP BY d.do_id
I you want to avoid duplicate values in the array, and as JSON_ARRAYAGG() (sadly) does not support the DISTINCT option, you can move aggregation to subqueries, like :
SELECT
d.do_id,
p.agg purpose,
e.agg expenses
FROM donations d
INNER JOIN (
SELECT do_id, JSON_ARRAYAGG(purpose) agg FROM purposes GROUP BY do_id
) p ON p.do_id = d.do_id
INNER JOIN (
SELECT do_id, JSON_ARRAYAGG(expense) agg FROM expense GROUP BY do_id
) e ON e.do_id = d.do_id
This demo on DB Fiddle returns :
| do_id | purpose | expenses |
| ----- | --------------------- | --------------------- |
| 2 | ["abc", "def", "gih"] | ["abc", "def", "gih"] |
| 3 | ["jkl"] | ["jkl"] |
1st Select Query Purposes
SELECT purposes.* FROM purposes
LEFT JOIN donations
ON purposes.do_id = donations.do_id
WHERE donations.do_id = '2' //This depends on the id of the donation
ORDER BY purposes.do_id ASC
2nd Select Query Expenses
SELECT expense.* FROM expense
LEFT JOIN donations
ON expense.do_id = donations.do_id
WHERE donations.do_id = '2' //This depends on the id of the donation
ORDER BY expense.ex_id ASC
All queries generated are from the table structure you've provided, but your question is quite vague!!

Is there a way to select data from one column in SQL table to get table with multiple columns?

So I have two SQL tables: one with id and in the other is meta value and meta_key. I joined them so it looks something like this:
+------+------------+----------+
| id | meta_value | meta_key |
+------+------------+----------+
| 1544 | product1 | 1 |
| 1544 | 2 | 2 |
| 1545 | product2 | 1 |
| 1545 | 5 | 2 |
| 1546 | product3 | 1 |
| 1546 | 10 | 2 |
+------+------------+----------+
And I want to get a query that would show me a table like this:
+------+------------+------------+
| id | meta_value | meta_value |
+------+------------+------------+
| 1544 | product1 | 2 |
| 1545 | product2 | 5 |
| 1546 | product3 | 10 |
+------+------------+------------+
The SQL query for first table looks like this:
SELECT
wprq_gf_entry.id,
wprq_gf_entry_meta.meta_value,
wprq_gf_entry_meta.meta_key
FROM
wprq_gf_entry
INNER JOIN wprq_gf_entry_meta ON wprq_gf_entry.id = wprq_gf_entry_meta.entry_id
WHERE
wprq_gf_entry_meta.form_id = 6
Is there a way to do this? ... Thank you!
You can use JOIN, but you need two of them:
SELECT e.*, m1.meta_value, m2.meta_value
FROM wprq_gf_entry e JOIN
wprq_gf_entry_meta m1
ON e.id = m1.entry_id AND m1.meta_key = 1 JOIN
wprq_gf_entry_meta m2
ON e.id = m2.entry_id AND m2.meta_key = 2;
You can use the table you created, let's call it joined_table.
With joined_table as(
SELECT
wprq_gf_entry.id,
wprq_gf_entry_meta.meta_value,
wprq_gf_entry_meta.meta_key
FROM wprq_gf_entry
INNER JOIN wprq_gf_entry_meta ON wprq_gf_entry.id
= wprq_gf_entry_meta.entry_id
WHERE wprq_gf_entry_meta.form_id = 6
)
Were dividing it into two tables - one with meta-key = 1 and the other with meta-key = 2.
Then, we are joining them
Select jt.id, jt.meta_value as meta_value_1,
jt2.meta_value as meta_value_2
From joined_table jt
Join joined_table jt2
On jt.id = jt2.id
And jt2.meta_key = 2
Where jt.meta_key = 1

Nested JOIN to create custom dynamic columns

I have a table veicoli (vehicles) like this:
-------------------------------
| ID | Modello | Targa |
-------------------------------
| 1 | IVECO | XA123WE |
-------------------------------
| 2 | IVECO | CF556XD |
-------------------------------
| 3 | FIAT | AS332ZZ |
-------------------------------
| 4 | GOLF | GF567YU |
-------------------------------
For each vehicle I have none, one or multiple revisioni_veicolo (revisions) (the one with bigger DateExpiring is the one I need to check if revision is still valid or not based on today date)
-------------------------------------------------------------------
| ID | veicoli_ID | DateExpiring | Pass_Success |
-------------------------------------------------------------------
| 1 | 1 | 2019-07-01 | 1
------------------------------------------------------------------
| 2 | 1 | 2020-10-01 | 0
-------------------------------------------------------------------
| 3 | 2 | 2019-11-25 | 1
-------------------------------------------------------------------
| 4 | 2 | 2018-10-20 | 1
-------------------------------------------------------------------
| 5 | 4 | 2017-10-20 | 1
-------------------------------------------------------------------
Based on my example above (today is 2019-10-29):
Vehicle: ID = 1 has a revision still active (2020-10-01) but not passed (Pass_success = 0)
Vehicle: ID = 2 has a revision still active (2019-11-25) and passed (Pass_success = 1)
Vehicle: ID = 3 has no revision yet
Vehicle: ID = 4 has revision, but no active revision (last expired on 2017-10-20) but the last one passed the check (Pass_success = 1)
What I need is to have 3 new custom columns created dynamically on my query result:
-------------------------------------------------------------------------------------------
| ID | Modello | Targa | RevisionPresent | RevisionStillActive | LastRevisionPassed |
-------------------------------------------------------------------------------------------
| 1 | IVECO | XA123WE | true | true | false
-------------------------------------------------------------------------------------------
| 2 | IVECO | CF556XD | true | true | true
-------------------------------------------------------------------------------------------
| 3 | FIAT | AS332ZZ | false | false | false
-------------------------------------------------------------------------------------------
| 4 | GOLF | GF567YU | true | false | true
-------------------------------------------------------------------------------------------
I tried to start with my old post: MYSQL INNER JOIN to get 3 types of result
But I'm very confused using nested JOIN
I tried starting a fiddle but i'm stuck on syntax error: http://sqlfiddle.com/#!9/3c70bf/2
You need a LEFT JOIN of the tables and conditional aggregation:
select v.ID, v.Modello, v.Targa,
max(r.DataScadenzaRevisione is not null) RevisionPresent,
coalesce(max(r.DataScadenzaRevisione >= current_date()), 0) RevisionStillActive,
max(case when r.DataScadenzaRevisione = g.maxdate then r.EsitoPositivo else 0 end) LastRevisionPassed
from veicoli v
left join revisioni_veicolo r on r.veicoli_ID = v.id
left join (
select veicoli_id, max(DataScadenzaRevisione) maxdate
from revisioni_veicolo
group by veicoli_id
) g on g.veicoli_ID = v.id
group by v.ID, v.Modello, v.Targa
See the demo.
Results:
| ID | Modello | Targa | RevisionPresent | RevisionStillActive | LastRevisionPassed |
| --- | ------- | ------- | --------------- | ------------------- | ------------------ |
| 1 | IVECO | XA123WE | 1 | 1 | 0 |
| 2 | IVECO | CF556XD | 1 | 1 | 1 |
| 3 | FIAT | AS332ZZ | 0 | 0 | 0 |
| 4 | GOLF | GF567YU | 1 | 0 | 1 |
...
LEFT JOIN (SELECT a.veicoli_ID, a.EsitoPositivo AS StatoUltimaRevisione,
a.DataScadenzaRevisione FROM revisioni_veicolo) a
...
There's two things wrong with this.
The alias a is defined for this subquery, so you can't reference it inside the subquery. But you don't need to qualify the columns in this subquery anyway - you didn't do this in other subqueries, so I'm not sure why you did it in this case.
You don't have any join condition for this join. MySQL is a little bit inconsistent about when join conditions are required. But in this case, you need one.
After I tested the query with these two corrections, it works.
Basically you just need to look at the last revision of each vehicule to produce that resultset.
You can do the filtering with a correlated subquery:
select
v.ID,
v.Modello,
v.Targa,
(DataScadenzaRevisione >= now()) RevisionPresent,
(DataScadenzaRevisione >= now() and EsitoPositivo = 1) RevisionStillActive,
(EsitoPositivo = 1) LastRevisionPassed
from
veicoli v
left join revisioni_veicolo r
on r.veicoli_ID = v.ID
and r.DataScadenzaRevisione = (
select max(DataScadenzaRevisione)
from revisioni_veicolo r1
where r1.veicoli_ID = v.ID
)
You can check the results with your sample data in this db fiddle.
Or you can use a window function (this requires MySQL 8.0):
select
v.ID,
v.Modello,
v.Targa,
(DataScadenzaRevisione >= now()) RevisionPresent,
(DataScadenzaRevisione >= now() and EsitoPositivo = 1) RevisionStillActive,
(EsitoPositivo = 1) LastRevisionPassed
from (
select
v.*,
r.*,
row_number() over(partition by ID order by r.DataScadenzaRevisione desc) rn
from veicoli v
left join revisioni_veicolo r on r.veicoli_ID = v.ID
) where coaelesce(rn, 1) = 1

MySQL select - get count by group

I have two tables joined and grouped, one is Orders the other one is Items.
What I want to get is the number of orders we have in x period.
So my initial thought was to first get the count of how may times each order occurs in the dataset, then divide each line by the above number, eg, order ID 1 occurs 3 times so the count would be .33 which * 3 = 1 order.
But i am having trouble at the first step, in getting the correct count of order.id occurrences.
What I would expect to show is like this:
+-------+---------+------+
|order |item |count |
|number | | |
+-------+---------+------+
| 1 | ABC | 3 |
+-------+---------+------+
| 1 | DEF | 3 |
+-------+---------+------+
| 1 | GHI | 3 |
+-------+---------+------+
| 2 | ABC | 2 |
+-------+---------+------+
| 2 | DEF | 2 |
+-------+---------+------+
| 3 | ABC | 1 |
+-------+---------+------+
but I am only getting it like this:
+-------+---------+------+
|order |item |count |
|number | | |
+-------+---------+------+
| 1 | ABC | 1 |
+-------+---------+------+
| 1 | DEF | 1 |
+-------+---------+------+
| 1 | GHI | 1 |
+-------+---------+------+
| 2 | ABC | 1 |
+-------+---------+------+
| 2 | DEF | 1 |
+-------+---------+------+
| 3 | ABC | 1 |
+-------+---------+------+
And this is my query:
SELECT
`orders`.`id` as 'order number',
`item`.`item`,
COUNT(`orders`.`id`) as ' count'
FROM `db`.`orders`
LEFT JOIN `item` ON `item`.`order_id` = `order`.`order_id`
GROUP BY `orders`.`id`, `item`.`item`
;
How?
Your query should be-
SELECT
`item`.`item`,
COUNT(`orders`.`id`) as 'count'
FROM `db`.`orders`
JOIN `item` ON `item`.`order_id` = `order`.`order_id`
GROUP BY `item`.`item`;
I don't think you need same item row multiple times as you are trying to get item wise count.
Update: You can get your desired results by this one
SELECT t1.order_id, t2.item, t1.cnt FROM
(
SELECT o.order_id,COUNT(i.items) cnt FROM
item i
JOIN orders o ON o.order_id=i.order_id
group by o.order_id
) t1
JOIN
item t2 ON t1.order_id=t2.order_id;
You should try following,
Select
`order`.`order_id`,
`item`.`item`,
`order`.`count`
from
(
SELECT
`orders`.`id` as order_id,
COUNT(*) as 'count'
FROM `db`.`orders`
group by `orders`.`id`
) as order
JOIN `item` ON `item`.`order_id` = `order`.`order_id`
After help from a friend, I ended up getting exactly what i wanted by using the following:
SELECT
orders.id as 'order number',
items.item,
(SELECT count(*) FROM db.items where items.id = sales.id) as 'order_count'
FROM db.orders
GROUP BY orders.id, items.item
;

Joining and nesting queries in mysql

Currently, I'm using this nice query:
select
users.name,
sum(race_results.winnings) as total_winnings,
count(CASE WHEN race_results.place=1 THEN 1 ELSE 0 END) AS times_won_first_place
from users
inner join race_results
where race_results.userid = users.id and race_results.place = 1
group by users.id
order by total_winnings desc
to get this
************************************************
| name | total_winnings | times_won_first_place |
| Bob | 4000 | 4 |
| John | 1000 | 1 |
************************************************
the race_results table looks like this
*******************************************
| id | raceid | userid | place | winnings |
| 1 | 1 | 1 | 1 | 1000 |
| 2 | 1 | 2 | 5 | 50 |
| 3 | 1 | 3 | 6 | 50 |
| 4 | 2 | 1 | 1 | 1000 |
| 5 | 2 | 2 | 3 | 250 |
*******************************************
I would like to include four three more columns for something like this
***************************************************************************
| name | total_winnings | total_races | 1st_place | 2nd_place | 3rd_place |
| Bob | 4000 | 5 | 4 | 0 | 0 |
| John | 1000 | 5 | 1 | 1 | 1 |
***************************************************************************
If I were to do separate queries for the new columns, I'd use
select count(raceid) from race_results where userid = 1
select count(raceid) from race_results where userid = 1 and place = 1
select count(raceid) from race_results where userid = 1 and place = 2
select count(raceid) from race_results where userid = 1 and place = 3
to do separate queries would be easy but with the existing query I had to use CASE just to get the count of times a user won 1st place. (using
count(CASE WHEN race_results.place=2 THEN 1 ELSE 0 END)
returns the same results).
How would I nest these or join them into my existing query to get what I want?
You can do it this way:
select
users.name,
sum(race_results.winnings) as total_winnings,
count(*) AS total_races,
sum(race_results.place = 1) AS times_won_first_place ,
sum(race_results.place = 2) AS times_won_second_place,
sum(race_results.place = 3) AS times_won_third_place
from users
inner join race_results
where race_results.userid = users.id
group by users.id
order by total_winnings desc;
With ANSI standard SQL you could use case expressions inside the sum function but since MySQL (and some other databases) evaluate boolean expressions to 1 for true you can replace the case expression with the just the condition to evaluate and then just sum them.
So instead of CASE WHEN race_results.place=1 THEN 1 ELSE 0 END you can do sum(race_results.place=1) and save some space and typing :)
See this SQL Fiddle for an example.