Getting sum of multiple columns and calculate each row's ratio - mysql

Below, is the schema of my brand_of_items table. For simplicity, shown here with two columns: id (primary and AI), symbol (varchar 50, unique)
Table - brand_of_items
id symbol
0 a
1 b
2 c
.. ..
10 j
Below, is the schema of my items_of_brand.
Table - mainIndexQuantity
id brand_of_items_id vol item_type salefinalizeddate
0 1 5 0 2005-5-11
1 1 6 0 2004-5-11
2 1 7 0 2011-5-11
3 1 8 0 2011-5-12
4 1 9 0 2011-5-12
5 1 10 0 2011-5-11
6 1 5 1 2012-5-11
7 1 6 1 2012-5-11
8 1 7 1 2011-5-11
9 1 8 1 2010-5-12
10 1 9 1 2012-5-12
11 1 10 1 2005-5-12
The mainIndexQuantity table brand_of_items_id columns is a foreign key which points to brand_of_items (id).
The mainIndexQuantity table item_type column is not a foreign key, which it should be.
The two item types are: 0 = retail and 1 = wholesale
I want to calculate the ratio of the types of items (retail vs wholesale) per each_brand_of_items table entry. The goal is to see if the a brands item is selling more in retail or wholesale.
**
Adding Complexity:
I want to add a date column to mainIndexQuantity table and want to find out the difference in sum of RetailVolume and WholesaleVolume and group the results by salefinalizeddate field.
This is to help determine what items in what seasons sold more and the (delta) difference in sum of RetailVolume & WholeSaleVolume will help to select items to pay most attention to.

Try this:
SELECT
b.id,
b.symbol,
IFNULL(SUM(m.item_type = 1), 0) / (COUNT(*) * 1.0) AS wholesaleRatio,
IFNULL(SUM(m.item_type = 0), 0) / (COUNT(*) * 1.0) AS RetailRatio
FROM brand_of_items b
LEFT JOIN mainIndexQuantity m ON b.id = m.brand_of_items_id
GROUP BY b.id,
b.symbol;
SQL Fiddle Demo.
This will give you:
| ID | SYMBOL | WHOLESALERATIO | RETAILRATIO |
----------------------------------------------
| 0 | a | 0 | 0 |
| 1 | b | 0.5 | 0.5 |
| 2 | c | 0 | 0 |
| 10 | j | 0 | 0 |
Assuming that:
wholesaleRatio is the count of the items of type Whole sale to the count of all items.
RetailRatio is the count of the items of type retail to the count of all items.
If this ration is for the total sum of the vol column to the total vol you can do this instead:
SELECT
b.id,
b.symbol,
SUM(CASE WHEN m.item_type = 1 THEN m.vol ELSE 0 END) / SUM(m.vol) AS wholesaleRatio,
SUM(CASE WHEN m.item_type = 0 THEN m.vol ELSE 0 END) / SUM(m.vol) AS RetailRatio
FROM brand_of_items b
LEFT JOIN mainIndexQuantity m ON b.id = m.brand_of_items_id
GROUP BY b.id,
b.symbol;
Note that:
I used LEFT JOIN, so that you got the unmatched rows in the result set, i.e, those brand items that has no entries the MainIndexQuantity table. If you don't want to include them, use INNER JOIN instead.
The multiply with 1.0 to get the count with decimal places, as noted by #JW.
Update 1
To include the Total Volume, Retail Volume Sum and Wholesale Volume sum try this:
SELECT
b.id,
b.symbol,
IFNULL(SUM(m.item_type = 1), 0) * 1.0 / COUNT(*) AS wholesaleRatio,
IFNULL(SUM(m.item_type = 0), 0) * 1.0 / COUNT(*) AS RetailRatio,
IFNULL(SUM(m.vol), 0) AS 'Total Volume',
SUM(CASE WHEN m.item_type = 1 THEN m.vol ELSE 0 END) AS 'Retail Volume sum',
SUM(CASE WHEN m.item_type = 1 THEN m.vol ELSE 0 END) AS 'Wholesale Volume sum'
FROM brand_of_items b
LEFT JOIN mainIndexQuantity m ON b.id = m.brand_of_items_id
GROUP BY b.id,
b.symbol;
Updated SQL Fiddle Demo.
This will give you:
| ID | SYMBOL | WHOLESALERATIO | RETAILRATIO | TOTAL VOLUME | RETAIL VOLUME SUM | WHOLESALE VOLUME SUM |
--------------------------------------------------------------------------------------------------------
| 0 | a | 0 | 0 | 0 | 0 | 0 |
| 1 | b | 0.5 | 0.5 | 90 | 45 | 45 |
| 2 | c | 0 | 0 | 0 | 0 | 0 |
| 10 | j | 0 | 0 | 0 | 0 | 0 |
If you want to sort the result set by these total and sums, put this query in a subquery, then you can do this:
SELECT *
FROM
(
SELECT
b.id,
b.symbol,
IFNULL(SUM(m.item_type = 1), 0) * 1.0 / COUNT(*) AS wholesaleRatio,
IFNULL(SUM(m.item_type = 0), 0) * 1.0 / COUNT(*) AS RetailRatio,
IFNULL(SUM(m.vol), 0) AS TotalVolume,
SUM(CASE WHEN m.item_type = 1 THEN m.vol ELSE 0 END) AS RetailVolumeSum,
SUM(CASE WHEN m.item_type = 1 THEN m.vol ELSE 0 END) AS WholesaleVolumeSum
FROM brand_of_items b
LEFT JOIN mainIndexQuantity m ON b.id = m.brand_of_items_id
GROUP BY b.id,
b.symbol
) AS sub
ORDER BY RetailVolumeSum DESC,
WholesaleVolumeSum DESC;
But your last requirement is not clear, are you looking for those brand of items that has the highest of retio/wholesale ratis and volumns or select the highest values of them?
For the later one:
SELECT *
FROM
(
SELECT
b.id,
b.symbol,
IFNULL(SUM(m.item_type = 1), 0) * 1.0 / COUNT(*) AS wholesaleRatio,
IFNULL(SUM(m.item_type = 0), 0) * 1.0 / COUNT(*) AS RetailRatio,
IFNULL(SUM(m.vol), 0) AS TotalVolume,
SUM(CASE WHEN m.item_type = 1 THEN m.vol ELSE 0 END) AS RetailVolumeSum,
SUM(CASE WHEN m.item_type = 1 THEN m.vol ELSE 0 END) AS WholesaleVolumeSum
FROM brand_of_items b
LEFT JOIN mainIndexQuantity m ON b.id = m.brand_of_items_id
GROUP BY b.id,
b.symbol
) AS sub
ORDER BY RetailVolumeSum DESC,
WholesaleVolumeSum DESC,
TotalVolume DESC
LIMIT 1;
Update 2
To get those brands that has the highest total volume, you can do this:
SELECT
b.id,
b.symbol,
IFNULL(SUM(m.item_type = 1), 0) * 1.0 / COUNT(*) AS wholesaleRatio,
IFNULL(SUM(m.item_type = 0), 0) * 1.0 / COUNT(*) AS RetailRatio,
IFNULL(SUM(m.vol), 0) AS TotalVolume,
SUM(CASE WHEN m.item_type = 1 THEN m.vol ELSE 0 END) AS RetailVolumeSum,
SUM(CASE WHEN m.item_type = 1 THEN m.vol ELSE 0 END) AS WholesaleVolumeSum
FROM brand_of_items b
LEFT JOIN mainIndexQuantity m ON b.id = m.brand_of_items_id
GROUP BY b.id,
b.symbol
HAVING SUM(m.vol) = (SELECT MAX(TotalVolume)
FROM
(
SELECT brand_of_items_id, SUM(vol) AS TotalVolume
FROM mainIndexQuantity
GROUP BY brand_of_items_id
) t);
Like this.
Note that:
This will give you the brands that has the highest total volume, if you are looking for those that has the highest ratio, you have to replace the having clause to get the max of the ratio rather than the max of total volume.
This will give you the items that have the highest total volume, so you might expect to have more than item, in case there was multiple items having the highest total volume, like in this updated fiddle demo. In this case, to get only one, you have to use LIMIT to return only one.

Related

Complex SQL Query for Inventory System

I have 5 tables:
Items
Inventory
ConsumedItemsMonitoring
DamagedItemsMonitoring
UnaccountedItems
I'm new to Complex SQL queries did some research and asked for help and this is what I my code looks like so far.
SELECT Items.ItemID, Items.Item,
SUM(CASE WHEN DATE(Inventory.ItemTransactionDate) < CURDATE() THEN Inventory.Quantity ELSE 0 END) -
SUM(CASE WHEN DATE(consumeditemmonitoring.TransactionDate) <CURDATE() THEN consumeditemmonitoring.Quantity ELSE 0 end) -
SUM(CASE WHEN DATE(damagedinventory.ItemTransactionDate)<CURDATE() THEN damagedinventory.Quantity ELSE 0 end) -
SUM(CASE WHEN DATE(unaccounteditems.ItemTransactionDate)<CURDATE() THEN unaccounteditems.Quantity ELSE 0 end) AS 'PrevBalance',
SUM(CASE WHEN DATE(Inventory.ItemTransactionDate)=CURDATE() THEN Inventory.Quantity else 0 END) AS 'DeliveredToday',
SUM(CASE WHEN DATE(damagedinventory.ItemTransactionDate)=CURDATE() THEN damagedinventory.Quantity ELSE 0 END) AS 'DamagedToday',
SUM(CASE WHEN DATE(consumeditemmonitoring.TransactionDate)=CURDATE() THEN consumeditemmonitoring.Quantity ELSE 0 END) AS 'ConsumedToday',
SUM(CASE WHEN DATE(unaccounteditems.ItemTransactionDate)=CURDATE() THEN unaccounteditems.Quantity ELSE 0 END) AS 'UnAccountedToday',
SUM(CASE WHEN DATE(Inventory.ItemTransactionDate) < CURDATE() THEN Inventory.Quantity else 0 end)-
SUM(CASE WHEN DATE(consumeditemmonitoring.TransactionDate) < CURDATE() THEN consumeditemmonitoring.Quantity ELSE 0 END)-
SUM(CASE WHEN DATE(damagedinventory.ItemTransactionDate) < CURDATE() THEN damagedinventory.Quantity ELSE 0 END)-
SUM(CASE WHEN DATE(unaccounteditems.ItemTransactionDate) < CURDATE() THEN unaccounteditems.Quantity ELSE 0 END)-
SUM(CASE WHEN DATE(consumeditemmonitoring.TransactionDate) = CURDATE() THEN consumeditemmonitoring.Quantity ELSE 0 END)-
SUM(CASE WHEN DATE(damagedinventory.ItemTransactionDate) = CURDATE() THEN damagedinventory.Quantity ELSE 0 END)-
SUM(CASE WHEN DATE(unaccounteditems.ItemTransactionDate) = CURDATE() THEN unaccounteditems.Quantity ELSE 0 END) +
SUM(CASE WHEN DATE(Inventory.ItemTransactionDate) = CURDATE() then Inventory.Quantity ELSE 0 end) AS 'Total Balance'
FROM Items
LEFT OUTER JOIN consumeditemmonitoring ON consumeditemmonitoring.ItemID = Items.ItemID
LEFT OUTER JOIN damagedinventory ON damagedinventory.ItemID = Items.ItemID
LEFT OUTER JOIN unaccounteditems ON unaccounteditems.ItemID = Items.ItemID
LEFT OUTER JOIN inventory ON inventory.ItemID= Items.ItemID
GROUP BY Items.ItemID
The output looks like some of the table are multiplied.
What you are seeing is a result of how joins work and the fact that the joins are executed before the group by. I can illustrate this with a simplified version of your data.
drop table if exists
items,
items_inventory,
items_consumed,
items_damaged,
items_unaccounted;
create table items (id int);
create table items_inventory(id int,itemid int,qty int);
create table items_consumed(id int,itemid int,qty int);
create table items_damaged(id int,itemid int,qty int);
create table items_unaccounted(id int,itemid int,qty int);
insert into items values(1),(2);
insert into items_inventory values (1,1,10),(2,1,10),(2,2,20);
insert into items_consumed values(1,1,5),(2,2,15);
insert into items_damaged values(1,1,25);
If we run a simple select
select i.id,
ii.id,ii.qty,
ic.id,ic.qty,
id.id,id.qty,
iu.id,iu.qty
from items i
left join items_inventory ii on ii.itemid = i.id
left join items_consumed ic on ic.itemid = i.id
left join items_damaged id on id.itemid = i.id
left join items_unaccounted iu on iu.itemid = i.id
;
we get 2 rows for item 1 even though there is only 1 row for items_consumed
+------+------+------+------+------+------+------+------+------+
| id | id | qty | id | qty | id | qty | id | qty |
+------+------+------+------+------+------+------+------+------+
| 1 | 1 | 10 | 1 | 5 | 1 | 25 | NULL | NULL |
| 1 | 2 | 10 | 1 | 5 | 1 | 25 | NULL | NULL |
| 2 | 2 | 20 | 2 | 15 | NULL | NULL | NULL | NULL |
+------+------+------+------+------+------+------+------+------+
3 rows in set (0.00 sec)
When we aggregate
select i.id,
count(*) as rows,
sum(ii.qty) as inventory,
sum(ic.qty) as consumed,
sum(id.qty) as damaged,
sum(iu.qty) as unaccounted
from items i
left join items_inventory ii on ii.itemid = i.id
left join items_consumed ic on ic.itemid = i.id
left join items_damaged id on id.itemid = i.id
left join items_unaccounted iu on iu.itemid = i.id
group by i.id;
we get 'doubling' up of consumed and damaged.
+------+------+-----------+----------+---------+-------------+
| id | rows | inventory | consumed | damaged | unaccounted |
+------+------+-----------+----------+---------+-------------+
| 1 | 2 | 20 | 10 | 50 | NULL |
| 2 | 1 | 20 | 15 | NULL | NULL |
+------+------+-----------+----------+---------+-------------+
2 rows in set (0.00 sec)
One way to deal with this is to aggregate BEFORE you join by pushing the aggregations into sub queries which you would then join. For example
select i.id, ii.inventory,ic.consumed,id.damaged,iu.unaccounted,
coalesce(ii.inventory,0)+coalesce(ic.consumed,0)+coalesce(id.damaged,0)+coalesce(iu.unaccounted,0) total
from items i
left join (select ii.itemid,sum(ii.qty) as inventory from items_inventory ii group by itemid) ii on ii.itemid = i.id
left join (select ic.itemid,sum(ic.qty) as consumed from items_consumed ic group by itemid) ic on ic.itemid = i.id
left join (select id.itemid,sum(id.qty) as damaged from items_damaged id group by itemid) id on id.itemid = i.id
left join (select iu.itemid,sum(iu.qty) as unaccounted from items_unaccounted iu group by itemid) iu on iu.itemid = i.id
;
+------+-----------+----------+---------+-------------+-------+
| id | inventory | consumed | damaged | unaccounted | total |
+------+-----------+----------+---------+-------------+-------+
| 1 | 20 | 5 | 25 | NULL | 50 |
| 2 | 20 | 15 | NULL | NULL | 35 |
+------+-----------+----------+---------+-------------+-------+
2 rows in set (0.00 sec)
Working Query thanks to sir #P.Salmon
SELECT I.ItemID,
I.Item,
COALESCE(II.InventoryPrevBal,0) - COALESCE(ICP.ConsumedPrevBal,0) - COALESCE(IDP.DamagedPrevBal,0) - COALESCE(IUP.UnaccountedPrevBal,0) PrevBalance,
COALESCE(II.InventoryBal,0) CurrentDelivered,
COALESCE(IC.Consumed,0) CurrentConsumed,
COALESCE(ID.Damaged,0) CurrentDamaged,
COALESCE(IU.Unaccounted,0) CurrentUnaccounted,
COALESCE(II.InventoryPrevBal,0) + COALESCE(II.InventoryBal,0) - COALESCE(ICP.ConsumedPrevBal,0) - COALESCE(IDP.DamagedPrevBal,0) - COALESCE(IUP.UnaccountedPrevBal,0) - COALESCE(IC.Consumed,0) - COALESCE(ID.Damaged,0) - COALESCE(IU.Unaccounted,0) CurrentTotal
FROM items I
LEFT JOIN (SELECT II.ItemID, SUM(CASE WHEN DATE(II.ItemTransactionDate) < CURDATE() THEN II.Quantity ELSE 0 END) as InventoryPrevBal, SUM(CASE WHEN DATE(II.ItemTransactionDate) = CURDATE() THEN II.Quantity ELSE 0 END) as InventoryBal FROM inventory II GROUP BY ItemID) II ON II.ItemID = I.ItemID
LEFT JOIN (SELECT ICP.ItemID, ICP.TransactionDate, SUM(ICP.Quantity) as ConsumedPrevBal FROM consumeditemmonitoring ICP WHERE DATE(ICP.TransactionDate) < CURDATE() GROUP BY ItemID) ICP ON ICP.ItemID = I.ItemID
LEFT JOIN (SELECT IDP.ItemID, IDP.ItemTransactionDate, SUM(IDP.Quantity) as DamagedPrevBal FROM damagedinventory IDP WHERE DATE(IDP.ItemTransactionDate) < CURDATE() GROUP BY ItemID) IDP ON IDP.ItemID = I.ItemID
LEFT JOIN (SELECT IUP.ItemID, IUP.ItemTransactionDate, SUM(IUP.Quantity) as UnaccountedPrevBal FROM unaccounteditems IUP WHERE DATE(IUP.ItemTransactionDate) < CURDATE() GROUP BY ItemID) IUP ON IUP.ItemID = I.ItemID
LEFT JOIN (SELECT IC.ItemID, IC.TransactionDate, SUM(IC.Quantity) as Consumed FROM consumeditemmonitoring IC WHERE DATE(IC.TransactionDate) = CURDATE() GROUP BY ItemID) IC ON IC.ItemID = I.ItemID
LEFT JOIN (SELECT ID.ItemID, ID.ItemTransactionDate, SUM(ID.Quantity) as Damaged FROM damagedinventory ID WHERE DATE(ID.ItemTransactionDate) = CURDATE() GROUP BY ItemID) ID ON ID.ItemID = I.ItemID
LEFT JOIN (SELECT IU.ItemID, IU.ItemTransactionDate, SUM(IU.Quantity) as Unaccounted FROM unaccounteditems IU WHERE DATE(IU.ItemTransactionDate) = CURDATE() GROUP BY ItemID) IU ON IU.ItemID = I.ItemID
ORDER BY I.Item ASC

MySql query to find difference between same column with condition

I have this table register:
id quantity type
1 | 10 | in
2 | 5 | in
1 | 3 | out
1 | 2 | out
2 | 5 | out
3 | 2 | in
3 | 1 | out
I want the balance of each stock *sum of type='in' - sum of type= 'out'*.
Desired output would be:
1 | 5
2 | 0
3 | 1
I also have another table item:
id | name
1 | A
2 | B
3 | C
Is it possible to view the output with the item name instead of the id?
So the final result is like:
A | 5
B | 0
C | 1
The basic idea is conditional aggregation --case inside of sum(). You also need a join to get the name:
select i.name,
sum(case when r.type = 'in' then quantity
when r.type = 'out' then - quantity
else 0
end) as balance
from register r join
item i
on r.id = i.id
group by i.name;
Acccording to description as mentioned in above question,as a solution to it please try executing following SQL query
SELECT i.name,
#in_total:= (select sum(quantity) from register where type = 'in'
and id = r.id group by id),
#out_total:= (select sum(quantity) from register where type = 'out'
and id = r.id group by id),
#balance:= (#in_total - #out_total) as balance
FROM `register`
as r join item i on r.id = i.id group by r.id
CROSS JOIN (SELECT #in_total := 0,
#out_total := 0,
#balance := 0) AS user_init_vars

MySQL : collect the sum of the associated values

I have three tables in database:
Table: article
id | code | name | quantity | stock_date
--------------------------------------------------
1 1dfod Article name 10 2016-04-01
Table: selling
id | client_id | selling_type_id | selling_date | selling_status
----------------------------------------------------------------
1 1 1 2016-04-02 1
2 1 1 2016-04-03 1
3 1 1 2016-04-04 1
Table: selling_detail
id | selling_id | article_id | quantity
-------------------------------------
1 1 1 2
2 1 1 3
3 1 1 1
4 2 1 3
5 3 1 1
at the end I would have a stock record for this article like this:
date | in_stock (item in stock) | out_stock (sum of item sold)
----------------------------------------------------------------------
2016-04-01 10 0
2016-04-02 0 6
2016-04-03 0 3
2016-04-04 0 1
All mysql queries to my knowledge do not give me this result.
Here is my code:
SELECT SUM(sd.quantity) out_stock, s.search_date, ifnull(ss.quantity, 0) in_stock
FROM selling_detail sd JOIN selling s ON (sd.selling_id = s.id)
LEFT JOIN shop_stock ss ON (ss.search_date = s.search_date) WHERE (sd.shop_stock_id = 1)
GROUP BY s.search_date;
SELECT date,SUM(in_stock) in_stock,SUM(out_stock) out_stock FROM
(
SELECT stock_date date,quantity in_stock,0 out_stock FROM article
UNION
SELECT selling_date,0,quantity FROM selling JOIN selling_detail ON selling_detail.selling_id = selling.id
) x
GROUP BY date;
As you are trying to combine similar data from two very different tables, you'll probably be staring down the barrel of a UNION ALL.
Something along these lines should get you started:
SELECT *
FROM (
SELECT a.stock_date `date`,
SUM(a.quantity) `in_stock (item in stock)`,
0 `out_stock (sum of item sold)`
FROM article a
WHERE a.id = :article_id
GROUP BY `date`
UNION ALL
SELECT s.selling_date,
0,
SUM(sd.quantity)
FROM selling s
JOIN selling_detail sd
ON sd.selling_id = s.id
AND sd.article_id = :article_id
/* WHERE s.selling_type = ??
AND s.selling_status = ?? /* If necessary */
GROUP BY `date`
) sr
ORDER BY `date`

MySQL: Multiple Running Totals from Different Subqueries

When I run a single query using the following formula to have the first column give back the month/year, the second give back the number of people signing per month, and the third give back the running total of signers, it works great:
SET #runtot1:=0;
SELECT
1rt.MONTH,
1rt.1signed,
(#runtot1 := #runtot1 + 1rt.1signed) AS 1rt
FROM
(SELECT
DATE_FORMAT(STR_TO_DATE(s.datecontacted,'%m/%d/%Y'),'%Y-%m') AS MONTH,
IFNULL(COUNT(DISTINCT CASE WHEN s.surveyid = 791796 THEN s.id ELSE NULL END),0) AS 1signed
FROM table1 s
JOIN table2 m ON s.id = m.id AND m.current = "Yes"
WHERE STR_TO_DATE(s.datecontacted,'%m/%d/%Y') > '2015-03-01'
GROUP BY MONTH
ORDER BY MONTH) AS 1rt
With the query above, I get the following results table, which would be exactly what I want if I only needed to count one thing:
MONTH 1signed 1rt
2015-03 0 0
2015-04 1 1
2015-05 0 1
2015-08 1 2
2015-10 1 3
2015-11 1 4
2016-01 0 4
2016-02 0 4
But I can't figure out how to do that with multiple subqueries since I need this to happen for multiple columns at the same time. For example, I was attempting things like this (which doesn't work):
SET #runtot1:=0;
SET #runtot2:=0;
select
DATE_FORMAT(STR_TO_DATE(s1.datecontacted,'%m/%d/%Y'),'%Y-%m') AS MONTH,
t1.1signed,
(#runtot1 := #runtot1 + t1.1signed) AS 1rt,
t2.2signed,
(#runtot2 := #runtot2 + t2.2signed) AS 2rt
from
(select
DATE_FORMAT(STR_TO_DATE(s.datecontacted,'%m/%d/%Y'),'%Y-%m') AS MONTH,
IFNULL(COUNT(DISTINCT CASE WHEN s.surveyid = 791796 THEN s.id ELSE NULL END),0) AS 1signed
from table1 s
left join table2 m ON m.id = s.id
where m.current = "Yes"
GROUP BY MONTH
ORDER BY MONTH) as T1,
(select
DATE_FORMAT(STR_TO_DATE(s.datecontacted,'%m/%d/%Y'),'%Y-%m') AS MONTH,
IFNULL(COUNT(DISTINCT CASE WHEN s.surveyid = 846346 THEN s.id ELSE NULL END),0) AS 2signed
from table1 s
left join table2 m ON m.id = s.id
where m.current = "Yes"
GROUP BY MONTH
ORDER BY MONTH) as T2,
table1 s1
LEFT JOIN table2 m1 ON m1.id = s1.id AND m1.current = "Yes"
WHERE STR_TO_DATE(s1.datecontacted,'%m/%d/%Y') > '2015-03-01'
GROUP BY DATE_FORMAT(STR_TO_DATE(s1.datecontacted,'%m/%d/%Y'),'%Y-%m')
ORDER BY DATE_FORMAT(STR_TO_DATE(s1.datecontacted,'%m/%d/%Y'),'%Y-%m')
That blew up my results badly -- I also tried LEFT JOINs to get those two next each other, but that didn't work either.
Here's a SQL Fiddle with a few values with the query at the top that works, but not the query needed to look like the idea below.
If the multiple subquery version of the code worked, below would be the ideal end-result:
MONTH 1signed 1rt 2signed 2rt
2015-03 0 0 1 1
2015-04 1 1 0 1
2015-05 0 1 1 2
2015-08 1 2 0 2
2015-10 1 3 0 2
2015-11 1 4 0 2
2016-01 0 4 0 2
2016-02 0 4 1 3
Just trying to figure out a way to get counts by month and rolling totals since March 2015 for two different survey questions using the same query. Any help would be greatly appreciated!
Your attempt was actually pretty close. I just got rid of S1 and joined the two subqueries together on their MONTH columns:
SET #runtot1:=0;
SET #runtot2:=0;
select
T1.MONTH,
t1.1signed,
(#runtot1 := #runtot1 + t1.1signed) AS 1rt,
t2.2signed,
(#runtot2 := #runtot2 + t2.2signed) AS 2rt
from
(select
DATE_FORMAT(STR_TO_DATE(s.datecontacted,'%m/%d/%Y'),'%Y-%m') AS MONTH,
IFNULL(COUNT(DISTINCT CASE WHEN s.surveyid = 791796 THEN s.id ELSE NULL END),0) AS 1signed
from table1 s
left join table2 m ON m.id = s.id
where m.current = "Yes" and STR_TO_DATE(s.datecontacted,'%m/%d/%Y') > '2015-03-01'
GROUP BY MONTH
ORDER BY MONTH) as T1,
(select
DATE_FORMAT(STR_TO_DATE(s.datecontacted,'%m/%d/%Y'),'%Y-%m') AS MONTH,
IFNULL(COUNT(DISTINCT CASE WHEN s.surveyid = 846346 THEN s.id ELSE NULL END),0) AS 2signed
from table1 s
left join table2 m ON m.id = s.id
where m.current = "Yes" and STR_TO_DATE(s.datecontacted,'%m/%d/%Y') > '2015-03-01'
GROUP BY MONTH
ORDER BY MONTH) as T2
WHERE
T1.MONTH=T2.MONTH
GROUP BY T1.MONTH
ORDER BY T1.MONTH
I haven't tested Strawberry's solution, which looks more elegant. But I thought you'd like to know that your approach (solving the running totals individually, then joining the results together) would have worked too.
It seems that you're after something like this...
The data set:
DROP TABLE IF EXISTS table1;
CREATE TABLE table1
( id INT NOT NULL
, date_contacted DATE NOT NULL
, survey_id INT NOT NULL
, PRIMARY KEY(id,survey_id)
);
DROP TABLE IF EXISTS table2;
CREATE TABLE table2
(id INT NOT NULL PRIMARY KEY
,is_current TINYINT NOT NULL DEFAULT 0
);
INSERT INTO table1 VALUES
(1,"2015-03-05",846346),
(2,"2015-04-15",791796),
(2,"2015-05-04",846346),
(3,"2015-06-07",791796),
(3,"2015-06-08",846346),
(4,"2015-08-02",791796),
(5,"2015-10-15",791796),
(6,"2015-11-25",791796),
(6,"2016-01-02", 11235),
(6,"2016-02-06",846346);
INSERT INTO table2 (id,is_current) VALUES
(1,1),
(2,1),
(3,0),
(4,1),
(5,1),
(6,1);
The query:
SELECT x.*
, #a:=#a+a rt_a
, #b:=#b+b rt_b
FROM
( SELECT DATE_FORMAT(date_contacted,'%Y-%m') month
, SUM(survey_id = 791796) a
, SUM(survey_id = 846346) b
FROM table1 x
JOIN table2 y
ON y.id = x.id
WHERE y.is_current = 1
GROUP
BY month
) x
JOIN (SELECT #a:=0,#b:=0) vars
ORDER
BY month;
+---------+------+------+------+------+
| month | a | b | rt_a | rt_b |
+---------+------+------+------+------+
| 2015-03 | 0 | 1 | 0 | 1 |
| 2015-04 | 1 | 0 | 1 | 1 |
| 2015-05 | 0 | 1 | 1 | 2 |
| 2015-08 | 1 | 0 | 2 | 2 |
| 2015-10 | 1 | 0 | 3 | 2 |
| 2015-11 | 1 | 0 | 4 | 2 |
| 2016-01 | 0 | 0 | 4 | 2 |
| 2016-02 | 0 | 1 | 4 | 3 |
+---------+------+------+------+------+

Mysql get sub list with count

I have table data in this form.
it w count
i1 a 2
i1 b 3
i2 a 1
i3 c 2
i4 b 1
When I query for a-b pair for each item (it) and respective count. If certain item (it) doesn't have both a and b then it is ignored (like i3). I need output in the form
it a b
i1 2 3
i2 1 0
i4 0 1
Any ideas how to solve this?
And I cannot store each word in different column. There are many words and mXn matrix of such a matrix is sparse.
Based on Saharsh's answer we could do it by
SELECT * FROM (SELECT a.it, SUM(CASE WHEN a.w = 'a' THEN a.count ELSE 0 END) AS a,
SUM(CASE WHEN a.w = 'b' THEN a.count ELSE 0 END) AS b
FROM tableA a WHERE a.w IN ('a', 'b') GROUP BY a.it) WHERE a>0 OR b>0
Is there a better way it terms of handling data with million rows?
You can do this with conditional aggregation:
select it, max(case when w = 'a' then count else 0 end) as a,
max(case when w = 'b' then count else 0 end) as b
from table t
where w in ('a', 'b')
group by it;
Try this:
SELECT a.it,
SUM(CASE WHEN a.w = 'a' THEN a.count ELSE 0 END) AS a,
SUM(CASE WHEN a.w = 'b' THEN a.count ELSE 0 END) AS b
FROM tableA a
WHERE a.w IN ('a', 'b')
GROUP BY a.it;
Check this SQL FIDDLE DEMO
OUTPUT
| IT | A | B |
|----|---|---|
| i1 | 2 | 3 |
| i2 | 1 | 0 |
| i4 | 0 | 1 |