How to aggregate and sum values of similar selects using SQL? - mysql

I have some similar selects that I'd like to join and aggregate values.
I've already tried with join, inner join, union, union all, without the expected result.
See that the selects are very similar. The differences are:
SUM(comumn_02)*.05 AS SUM_01 different from SUM(comumn_02)*.45 AS SUM_02
and AND COLUMN_05 = ALFA different from AND COLUMN_05 = BETA in the second select.
The whole thing is here:
SELECT
COLUMN_01,
SUM(COLUMN_02)*.05 AS SUM_01
FROM table
WHERE
COLUMN_03 = 1
AND COLUMN_04 = 2
AND COLUMN_05 = ALFA
GROUP BY COLUMN_01
UNION ALL
SELECT
COLUMN_01,
SUM(COLUMN_02)*.45 AS SUM_02
FROM table
WHERE
COLUMN_03 = 1
AND COLUMN_04 = 2
AND COLUMN_05 = BETA
GROUP BY COLUMN_01;
In this example we have the following result:
|-----------|--------|
| COLUMN_01 | SUM_01 |
|-----------|--------|
| value_01 | 465 |
| value_02 | 186 |
| value_03 | 245 |
| value_01 | 102 |
| value_02 | 108 |
| value_03 | 325 |
|--------------------|
But what I'd like to have would be:
|-----------|--------|
| COLUMN_01 | SUM_01 |
|-----------|--------|
| value_01 | 567 | //sum of 465 + 102
| value_02 | 294 | //sum of 186 + 108
| value_03 | 570 | //sum of 245 + 570
|--------------------|
And most important, would this be scalable? i.e. to use it with three or more select "unions" or the performance would decrease a lot?

The problem in your query is that you're aggregating on the single tables, then you are using UNION ALL to get your values. In order to solve your problem, you need to invert the order of these two operations:
first you apply the UNION function
then you aggregate with the SUM aggregation function
WITH CTE AS(
SELECT COLUMN_01,
COLUMN_02*.05 AS COLUMN
FROM table
WHERE COLUMN_03 = 1
AND COLUMN_04 = 2
AND COLUMN_05 = ALFA
UNION ALL
SELECT COLUMN_01,
COLUMN_02*.45 AS COLUMN
FROM table
WHERE COLUMN_03 = 1
AND COLUMN_04 = 2
AND COLUMN_05 = BETA
)
SELECT COLUMN_01,
SUM(COLUMN) AS SUM_01
FROM CTE
GROUP BY COLUMN_01

Use conditional aggregation:
SELECT COLUMN_01,
SUM(COLUMN_02 * CASE COLUMN_05 WHEN 'ALFA' THEN 0.05 WHEN 'BETA' THEN 0.45 END) AS SUM_01
FROM tablename
WHERE COLUMN_03 = 1 AND COLUMN_04 = 2 AND (COLUMN_05 IN ('ALFA', 'BETA'))
GROUP BY COLUMN_01;

Related

select max from multiple columns mysql with corresponding column value

I have a query result in MySQL with the following structure
class | eng | math | sci | ss | kisw
-------+------------+-------------------
4N | 80.2 | 41.2 |96.3 |52.0 | 41.5
4S | 52.3 | 45.2 |98.5 |65.2 | 85.3
5N | 74.3 | 87.0 |69.9 |74.2 | 84.5
5S | 87.5 | 45.6 |72.3 |25.6 | 10.3
The above query gives subject scores for each of the classes. I want to perform a query that will give me the best class per subject. This is what the end result should look like :
subject | class | score
--------+-------+-------
eng | 5S | 87.5
math | 5N | 87.0
sci | 4S | 98.5
ss | 5N | 74.2
kisw | 4S | 85.3
I have tried looking at these questions but none of the answers addresses the issue of selecting a maximum value for many columns with a corresponding column value
Select MAX value from column and corresponding value from another
You can try using union all
select 'eng' as subject,class,eng from tablename
where eng =(select max(eng) from tablename)
union all
select 'math',class,math from tablename
where math=(select max(math) from tablename)
union all
select 'sci',class,sci from tablename
where sci=(select max(sci) from tablename)
union all
select 'ss',class,ss from tablename
where ss=(select max(ss) from tablename)
union all
select 'kisw', class,kisw from tablename
where kisw=(select max(kisw) from tablename)
SELECT *
FROM (SELECT subject, class, score
FROM TABLE )
PIVOT (MAX(score) AS max_score FOR (subject) IN ('eng' AS eng, 'math' AS math, 'sci' AS sci, 'ss' as ss , 'kisw' as kisw));

Mysql join select max for all record

I am unable to map the record as my expectation.
Doc Table
-------+-------------------
doc_id | doc_title
-------+-------------------
1 | My book
-------+-------------------
2 | My sec Book
--------------------------
Doc details Table
-----------+--------------+-----------------------
fk_doc_id | doc_version | submit_date
-----------+--------------+-----------------------
1 | 1 | 2015-10-25 14:32:01
-----------+--------------+-----------------------
1 | 2 | 2015-10-26 13:00:01
-----------+--------------+-----------------------
1 | 3 | 2015-10-27 09:00:00
--------------------------+-----------------------
2 | 1 | 2015-10-25 11:15:01
-----------+--------------+-----------------------
2 | 2 | 2015-10-26 10:00:00
--------------------------+-----------------------
Question: How do I join this two tables to get each documents with the latest version doc info? even though I get the latest version but the row info which is not correct.
So far I have tried this query
SELECT *, max(doc_version) AS latest_version
FROM d_doc
JOIN d_doc_dtl ON d_doc.doc_id = d_doc_dtl.fk_doc_id
GROUP BY d_doc.doc_id;
My expected result is
--------+--------------+----------------+--------------------
doc_id | doc_title | latest_version | submit_date
--------+--------------+----------------+--------------------
1 | My book | 3 | 2015-10-27 09:00:00
--------+--------------+----------------+--------------------
2 | My sec book | 2 | 2015-10-26 10:00:00
----------------------------------------+--------------------
but my result is
--------+--------------+----------------+--------------------
doc_id | doc_title | latest_version | submit_date
--------+--------------+----------------+--------------------
1 | My book | 3 | 2015-10-25 14:32:01
--------+--------------+----------------+--------------------
2 | My sec book | 2 | 2015-10-25 11:15:01
----------------------------------------+--------------------
NOTE: the submit_date which is no correct.
SELECT d_doc.doc_id, d_doc.doc_title, max_table.latest_version
FROM d_doc JOIN (
select fk_doc_id, max(doc_version) as latest_version from d_doc_dtl group by fk_doc_id
) as max_table ON d_doc.doc_id = max_table.fk_doc_id
This query should work as you expect. It selects latest document versions in inner subquery and than joins it with documents.
SELECT d.doc_id,
d.doc_title,
dtl.doc_version latest_version,
dtl.submit_date
FROM d_doc d
INNER JOIN (SELECT dt.*
FROM d_doc_dtl dt
INNER JOIN (SELECT fk_doc_id, MAX(doc_version) doc_version
FROM d_doc_dtl
GROUP BY fk_doc_id) dm
ON dt.fk_doc_id = dm.fk_doc_id
AND dt.doc_version = dm.doc_version) dtl
ON d.doc_id = dtl.fk_doc_id
You get wrong results because you selected only max(version), but date as it is not in group by clause can contain any value. First you need to get records containing latest version as shown above.
Easy, instead of
SELECT *, max(doc_version) AS latest_version
Use this
SELECT d_doc.*, max(doc_version) AS latest_version
What you were doing by selecting * is getting all the results after the table is joined and you only wanted the original table results.
select * from doc_table , doc_version where exists( select
max(version_id)
from
doc_version vert
where
(doc_table .DOC_ID = vert.VERSION_DOC_ID) ) group by doc_id;
You can try something like this.

MySQL: Query form two separate ID lists and make one unified list, without duplicate values

I have a table as below and I am trying to merge Block user and block by columns and remove intersect values and create a list of unique numbers, but literally i am failing to get a specific users block list.
|------------------------------------------------------
|block_id | block_user | block_by | block_at
|------------------------------------------------------
| 1 | 22 | 1 | 1434451573
| 7 | 59 | 1 | 1434695298
| 10 | 4 | 1 | 1434695327
| 11 | 1 | 14 | 1434695349
I need something as following, it needs to query by the id of 1 ;
|-----------
|block_list
|-----------
| 4
| 14
| 22
| 59
I am trying the SQL as following and literally i am failed to get the desired results;
SELECT block_user AS blocked_user FROM block_list
UNION
SELECT block_by AS blocked_list FROM block_list
WHERE (block_user = '1' OR block_by = '1')
If 1 is always to be excluded you can simply use this:
SELECT *
FROM (
SELECT block_user as id
FROM yourTable
UNION
SELECT block_by as id
FROM yourTable
) as dat
WHERE dat.id <> 1
Maybe you want to add your WHERE block_user = 1 to the subquery if it's needed in your logic. This wasn't really clear.
Try something like:
SELECT block_user
FROM (
SELECT block_user AS blocked_user FROM block_list
UNION
SELECT block_by AS blocked_user FROM block_list
WHERE (block_user = '1' OR block_by = '1'))
WHERE block_user != '1'

mySql query - subtract resulted value

I need a bit of help on a mathematical function,
Following query will result 2 lines per lot like this:
group | style | lot | section | q1 | q2 |q3 | q4 | ...
aaaaa | sssss | 123 | 111111 | 55 | 77 | 88 | 99 | ...
aaaaa | sssss | 123 | 222222 | 10 | 20 | 20 | 10 | ...
aaaaa | sssss | 321 | 111111 | 11 | 22 | 44 | 55 | ...
aaaaa | sssss | 321 | 222222 | 10 | 23 |33 | 10 | ...
each lot result 2 diff section code (2 lines)
Question is : how do I make a subtraction between the 2 section code for the colums q1 q2 q3 q4 q5 ... ?
expected results:
group | style | lot | q1 | q2 |q3 | q4 | ...
aaaaa | sssss | 123 | 45 | 57 |68 | 89 | ...
aaaaa | sssss | 321 | 1 | -1 |11 | 45 | ...
query so far :
SELECT DISTINCT gp_style_gr.code_groupe, po_lot.num_style, po_lot_sp.Num_lot,
po_lot_sp.num_secti, po_lot_se.code_secti, po_lot.terminer, po_lot.date_livraison,
po_lot_sp.qte_1, po_lot_sp.qte_2, po_lot_sp.qte_3, po_lot_sp.qte_4, po_lot_sp.qte_5,
po_lot_sp.qte_6, po_lot_sp.qte_7, po_lot_sp.qte_8, po_lot_sp.qte_9, po_lot_sp.qte_10,
po_lot_sp.qte_11, po_lot_sp.qte_12, po_lot_sp.qte_13, po_lot_sp.qte_14, po_lot_sp.qte_15,
po_lot_sp.qte_16, po_lot_sp.qte_17, po_lot_sp.qte_18, po_lot_sp.qte_19, po_lot_sp.qte_20,
po_lot_sp.qte_21, po_lot_sp.qte_22, po_lot_sp.qte_23, po_lot_sp.qte_24, po_lot_sp.qte_25,
po_lot_sp.qte_26, po_lot_sp.qte_27, po_lot_sp.qte_28, po_lot_sp.qte_29, po_lot_sp.qte_30
FROM po_lot_sp
LEFT OUTER JOIN po_lot_se ON po_lot_se.num_lot = po_lot_sp.num_lot
and po_lot_se.num_secti = po_lot_sp.num_secti
LEFT OUTER JOIN po_lot ON po_lot.num_lot = po_lot_sp.num_lot
LEFT OUTER JOIN gp_style_gr ON gp_style_gr.num_style = po_lot.num_style
WHERE
((gp_style_gr.code_groupe = 'INSTOCK') and (po_lot.terminer = '0')
and (po_lot_se.code_secti = '01')) or ((gp_style_gr.code_groupe = 'INSTOCK')
and (po_lot.terminer = '0') and (po_lot_se.code_secti = '09'))
ORDER BY gp_style_gr.code_groupe, po_lot.num_style, po_lot_sp.Num_lot,
po_lot_sp.num_secti, po_lot_se.code_secti, po_lot.terminer, po_lot.date_livraison,
Thanks !
If the section code follows some pattern as it does in your example, then you can simply join the table against itself.
I'll pretend your table is called po_lot_sp as it is in your example.
In the following query, I assume that the second row has a higher section number. That's the condition t1.section > t2.section. If not, change appropriately. If the section numbers follow no pattern, then ignore this completely.
SELECT t1.`group`, t1.style, t1.lot, t1.section,
t2.q1 - t1.q1 q1, t2.q2 - t1.q2 q2, t2.q3 - t1.q3 q3, t2.q4 - t1.q4 q4
FROM t t1
JOIN t t2 ON t1.`group` = t2.`group` AND t1.style = t2.style AND
t1.lot = t2.lot AND t1.section > t2.section
Fiddle here.
This is the fastest way I can think of. Of course, this is assuming that you want to decrement from section with value '111111':
SELECT `group`, style, lot,
sum(if(section = '111111', q1, -q1)),
sum(if(section = '111111', q2, -q2)),
sum(if(section = '111111', q3, -q3)),
sum(if(section = '111111', q4, -q4))
FROM t
GROUP BY `group`, style, lot
Fiddle here.
By the way, try not to use group as a column name. It is a reserved word.
If you don't know the section value you want to decrement from and you only want to decrement from the lowest section, then go for Andy's solution.
Try this:
select a.group,a.style,a.lot,
coalesce(a.q1 -
(select b.q1 from tablename b where b.ID = a.ID + 1), a.q1) as q1,
coalesce(a.q2-
(select b.q2 from tablename b where b.ID = a.ID + 1), a.q2) as q2,
coalesce(a.q3-
(select b.q3 from tablename b where b.ID = a.ID + 1), a.q3) as q3,
coalesce(a.q4-
(select b.q4 from tablename b where b.ID = a.ID + 1), a.q4) as q4
from tablename a group by a.lot
Note: Here ID refers to a primary key from your table and tablename refers to your original table name.So replace the field ID with your primary key field and table name vice-versa.
Demo

Filter query with order by and limit when using left join

How to filter query with order by and limit when using left join
store_profile
id + store_name
1 | Accessorize.me
2 | Active IT
3 | Edushop
4 | Gift2Kids
5 | Heavyarm
6 | Bamboo
store_fee
id + store_id + date_end
1 | 1 | 27-6-2013
2 | 2 | 29-8-2013
3 | 3 | 02-6-2013
4 | 4 | 20-4-2013
5 | 4 | 01-7-2013
6 | 4 | 28-9-2013
7 | 5 | 03-9-2013
8 | 6 | 01-9-2013
my previous query
$order_by_for_sort_column = "order by $column" //sorting column
$query = "SELECT * FROM store_profile sp LEFT JOIN store_fee sf ON (sf.store_id = sp.id) $order_by_for_sort_column";
what i want is order by id desc and limit 1 for table store_fee not for for entire query. So i can grab the latest date in date_end for each store.
As you can see for store_id 4(store_fee) i have 3 different date and i just want grab the latest date.
and the result should be something like this
1 | Accessorize.me 27-6-2013
2 | Active IT 29-8-2013
3 | Edushop 02-6-2013
4 | Gift2Kids 28-9-2013
5 | Heavyarm 03-9-2013
6 | Bamboo 01-9-2013
SELECT a.id, a.store_name, MAX(b.date_End) date_end
FROM store_profile a
LEFT JOIN store_fee b
ON a.ID = b.store_ID
GROUP BY a.id, a.store_name
SQLFiddle Demo
but if the datatype date_End column is varchar, the above query won't work because it sorts the value by character and that it can mistakenly gives undesired result. 18-1-2013 is greater than 01-6-2013.
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
SELECT *
FROM store_profile AS sp
LEFT JOIN (
SELECT store_id, MAX(date_end)
FROM store_fee
GROUP BY store_id
) AS sf
ON sp.id=sf.store_id;