MySQL Group by week num w/ multiple date column - mysql

I have a table with columns similar to below , but with about 30 date columns and 500+ records
id | forcast_date | actual_date
1 10/01/2013 12/01/2013
2 03/01/2013 06/01/2013
3 05/01/2013 05/01/2013
4 10/01/2013 09/01/2013
and what I need to do is get a query with output similar to
week_no | count_forcast | count_actual
1 4 6
2 5 7
3 2 1
etc
My query is
SELECT weekofyear(forcast_date) as week_num,
COUNT(forcast_date) AS count_forcast ,
COUNT(actual_date) AS count_actual
FROM
table
GROUP BY
week_num
but what I am getting is the forcast_date counts repeated in each column, i.e.
week_no | count_forcast | count_actual
1 4 4
2 5 5
3 2 2
Can any one please tell me the best way to formulate the query to get what I need??
Thanks

try:
SELECT weekofyear(forcast_date) AS week_forcast,
COUNT(forcast_date) AS count_forcast, t2.count_actual
FROM
t t1 LEFT JOIN (
SELECT weekofyear(actual_date) AS week_actual,
COUNT(forcast_date) AS count_actual
FROM t
GROUP BY weekOfYear(actual_date)
) AS t2 ON weekofyear(forcast_date)=week_actual
GROUP BY
weekofyear(forcast_date), t2.count_actual
sqlFiddle
You have to write about 30 (your date columns) left join, and the requirement is that your first date column shouldn'd have empty week (with a count of 0) or the joins will miss.

Try:
SELECT WeekInYear, ForecastCount, ActualCount
FROM ( SELECT A.WeekInYear, A.ForecastCount, B.ActualCount FROM (
SELECT weekofyear(forecast_date) as WeekInYear,
COUNT(forecast_date) as ForecastCount, 0 as ActualCount
FROM TableWeeks
GROUP BY weekofyear(forecast_date)
) A
INNER JOIN
( SELECT * FROM
(
SELECT weekofyear(forecast_date) as WeekInYear,
0 as ForecastCount, COUNT(actual_date) as ActualCount
FROM TableWeeks
GROUP BY weekofyear(actual_date)
) ActualTable ) B
ON A.WeekInYear = B.WeekInYear)
AllTable
GROUP BY WeekInYear;
Here's my Fiddle Demo

Just in case someone else comes along with the same question:
Instead of trying to use some amazing query, I ended up creating an array of date_columns_names and a loop in the program that was calling this query, and for each date_column_name, performing teh asme query. It is a bit slower, but it does work

Related

configure query to bring rows which have more than 1 entries

How to get those entries which have more than 1 records?
If it doesn't make sense... let me explain:
From the below table I want to access the sum of the commission of all rows where type is joining and "they have more than 1 entry with same downmem_id".
I have this query but it doesn't consider more entries scenario...
$search = "SELECT sum(commission) as income FROM `$database`.`$memcom` where type='joining'";
Here's the table:
id mem_id commission downmem_id type time
2 1 3250 2 joining 2019-09-22 13:24:40
3 45 500 2 egbvegr new time
4 32 20 2 vnsjkdv other time
5 23 2222 2 vfdvfvf some other time
6 43 42 3 joining time
7 32 353 5 joining time
8 54 35 5 vsdvsdd time
Here's the expected result: it should be the sum of the id no 2, 7 only
ie. 3250+353=whatever.
It shouldn't include id no 6 because it has only 1 row with the same downmem_id.
Please help me to make this query.
Another approach is two levels of aggregation:
select sum(t.commission) income
from (select sum(case when type = 'joining' then commission end) as commission
from t
group by downmem_id
having count(*) > 1
) t;
The main advantage to this approach is that this more readily supports more complex conditions on the other members of each group -- such as at most one "joining" record or both "joining" records and no more than two "vnsjkdv" records.
Use EXISTS:
select sum(t.commission) income
from tablename t
where t.type = 'joining'
and exists (
select 1 from tablename
where id <> t.id and downmem_id = t.downmem_id
)
See the demo.
Results:
| income |
| ----- |
| 3603 |
You can use subquery that will find all downmem_id having more than one occurrence in the table.
SELECT Sum(commission) AS income
FROM tablename
WHERE type = 'joining'
AND downmem_id IN (SELECT downmem_id
FROM tablename t
GROUP BY downmem_id
HAVING Count(id) > 1);
DEMO

mysql: two table join with sum

I'm attempting to join two tables and also get a SUM and flailing badly. I need to get the total commission amounts for each affiliate where affiliate.approved=1 AND order.status=3.
//affiliate table
affiliate_id | firstname | lastname | approved |
1 joe shmoe 1
2 frank dimag 0
3 bob roosky 1
here's the order table
//order
affiliate_id | order_status_id | commission
1 3 0.20
1 0 0.30
2 3 0.10
3 3 0.25
1 3 0.25
2 3 0.15
2 0 0.20
and here's what I'd like the query to return:
affiliate_id | commission
1 0.45
3 0.25
Here is my attempt that doesn't work. It outputs just one line.
SELECT order.affiliate_id, SUM(order.commission) AS total, affiliate.firstname, affiliate.lastname FROM `order`, `affiliate` WHERE order.order_status_id=3 AND affiliate.approved=1 AND order.affiliate_id = affiliate.affiliate_id ORDER BY total;
thanks for any help.
You've missed GROUP BY, try this:
SELECT
`order`.affiliate_id,
SUM(`order`.commission) AS total,
affiliate.firstname,
affiliate.lastname
FROM `order`
JOIN `affiliate`
ON `order`.order_status_id = 3 AND affiliate.approved = 1 AND `order`.affiliate_id = affiliate.affiliate_id
GROUP BY `order`.affiliate_id
ORDER BY total;
Demo Here
You can try this Query for your solution :-
SELECT order.affiliate_id, SUM(order.commission) AS total,affiliate.firstname,
affiliate.lastname
FROM `order`, `affiliate`
WHERE order.order_status_id=3
AND affiliate.approved=1
AND order.affiliate_id = affiliate.affiliate_id
GROUP BY order.affiliate_id
ORDER BY total;
Here is the solution:
select affiliate.affiliate_id,sum(`order`.commission) as total from affiliate left join `order` on affiliate.affiliate_id=`order`.affiliate_id
where affiliate.approved=1 and `order`.order_status_id=3 group by affiliate.affiliate_id
In addition,"order" is a key word of SQL , I recommend you not to use it as a table/column name.
First: Remove the implicit join syntax. It's confusing.
Second: You needed to group by affiliate_id. Using aggregate function without group by collapses your result set into a single row.
Here's the query using INNER JOIN:
SELECT
`order`.affiliate_id,
SUM(`order`.commission) AS total,
affiliate.firstname,
affiliate.lastname
FROM `order`
INNER JOIN`affiliate` ON `order`.affiliate_id = affiliate.affiliate_id
WHERE `order`.order_status_id = 3
AND affiliate.approved = 1
GROUP BY affiliate.affiliate_id
ORDER BY total;
WORKING DEMO
Caution: You have picked one of the reserved words of MySQL as table name (order). Be aware to enclose it with (`)backtick always .
Just a gentle reminder

Select sum of zero if no records in second table?

I did some research and learned about the COALESCE(sum(num), 0) function. The issue is the example I found only related to using one table.
I am calculating a sum from a second table, and if there are no records for an item in the second table, I still want it to show up in my query and have a sum of zero.
SELECT note.user, note.product, note.noteID, note.note, COALESCE(sum(noteTable.Score), 0) as points
FROM note, noteTable
WHERE note.user <> 3 AND note.noteID = noteTable.noteID
I am only recieving results if there is an entry in the second table noteTable. If there are scores added for a note, I still want them to show up in the result with a points value of zero.
Table Examples:
Note
user | product | noteID |note
3 1 1 Great
3 2 2 Awesome
4 1 3 Sweet
NoteTable
noteID | score
1 5
The query should show me this:
user | noteID | sum(points)
3 1 5
3 2 0
4 3 0
But I am only getting this:
user | noteID | sum(points)
3 1 5
http://sqlfiddle.com/#!9/aae812/2
SELECT
note.user,
note.product,
note.noteID, note.note,
COALESCE(sum(noteTable.Score),0) as points
FROM note
LEFT JOIN noteTable
ON note.noteID = noteTable.noteID
WHERE note.user <> 3
and I guess you should add:
GROUP BY note.noteid
if you expect to get SUM for every user. So you want to get more then 1 record back.
First, learn to use proper JOIN syntax and table aliases. The answer to your question is SUM() along with COALESCE():
SELECT n.user, n.product, n.noteID, n.note,
COALESCE(sum(nt.Score), 0) as points
FROM note n LEFT JOIN
noteTable nt
ON n.noteID = nt.noteID
WHERE n.user <> 3
GROUP BY n.user, n.product, n.noteID, n.note;
You also need a GROUP BY.

MySQL Winning Streak for every Player

I have a table with winner and loser statistics from a game:
id winner_id loser_id
1 1 2
2 1 2
3 3 4
4 4 3
5 1 2
6 2 1
7 3 4
8 3 2
9 3 5
10 3 6
11 2 3
12 3 6
13 2 3
I want a result table where i can find the highest winning streak of every player in the game. A streak of a player is broken, when he lost a game (player_id = loser_id). It should look like:
player_id win_streak
1 3
2 2
3 4
4 1
5 0
6 0
I tried many queries with user defined variables etc. but i can't find a solution. Thanks!
SQL Fiddle : http://sqlfiddle.com/#!9/3da5f/1
Is this the same as Alex's approach; I'm not quite sure, except that it seems to have one distinct advantage.... ;-)
SELECT player_id, MAX(CASE WHEN result = 'winner' THEN running ELSE 0 END) streak
FROM
( SELECT *
, IF(player_id = #prev_player,IF(result=#prev_result,#i:=#i+1,#i:=1),#i:=1) running
, #prev_result := result
, #prev_player:=player_id
FROM
( SELECT id, 'winner' result, winner_id player_id FROM my_table
UNION
SELECT id, 'loser', loser_id FROM my_table
) x
,
( SELECT #i:=1,#prev_result = '',#prev_player:='' ) vars
ORDER
BY x.player_id
, x.id
) a
GROUP
BY player_id;
I guess you should better to do that on php (or any other language you use) side.
But just to give you some idea and as experiment and example for some unique cases (hope it could be useful somewhere)
Here is my approach:
http://sqlfiddle.com/#!9/57cc65/1
SELECT r.winner_id,
(SELECT MAX(IF(winner_id=r.winner_id,IF(#i IS NULL, #i:=1,#i:=#i+1), IF(loser_id = r.winner_id, #i:=0,0)))
FROM Results r1
WHERE r1.winner_id = r.winner_id
OR r1.loser_id = r.winner_id
GROUP BY IF(winner_id=r.winner_id, winner_id,loser_id)) win_streak
FROM ( SELECT winner_id
FROM Results
GROUP BY winner_id
) r
It returns not all ids now but only who had ever win. So to make it better, probably you have user table. If so it would simplify a query. If you have no user table you need to union all somehow users who had never win.
You are welcome if any questions.

Get last 7 days count even when there are no records

How can I make a SQL query that returns me something like
---------------------
|DATE | Count |
---------------------
|2015/01/07 | 7 |
|2015/01/06 | 0 |
|2015/01/05 | 8 |
|2015/01/04 | 5 |
|2015/01/03 | 0 |
|2015/01/02 | 4 |
|2015/01/01 | 2 |
---------------------
When there are no records for the 6th and 3rd?
You need a table of all the sequence numbers from 0 to 6. This is easy to generate in a simple query, as follows.
SELECT 0 AS seq
UNION ALL SELECT 1 UNION ALL SELECT 2
UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6
Next, let's use this to construct a virtual table of seven dates. For this example, we pick today and the six preceding days.
SELECT DATE(NOW())-INTERVAL seq.seq DAY theday
FROM (
SELECT 0 AS seq
UNION ALL SELECT 1 UNION ALL SELECT 2
UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6
) seq
Then you do your summary query. You didn't say exactly how it goes so I will guess. This one gives you the records from six days ago until today. Today is still in progress.
SELECT DATE(i.item_time) theday
COUNT(*) `count`
FROM items i
WHERE i.item_time >= DATE(NOW()) - INTERVAL 6 DAYS
GROUP BY DATE(i.item_time)
Finally, starting with the list of days, let's LEFT JOIN that summary to it.
SELECT thedays.theday, IFNULL(summary.`count`,0) `count`
FROM (
SELECT DATE(NOW())-INTERVAL seq.seq DAY theday
FROM (
SELECT 0 AS seq
UNION ALL SELECT 1 UNION ALL SELECT 2
UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6
) seq
) thedays
LEFT JOIN (
SELECT DATE(i.item_time) theday
COUNT(*) `count`
FROM items i
WHERE i.item_time >= DATE(NOW()) - INTERVAL 6 DAYS
GROUP BY DATE(i.item_time)
) summary USING (theday)
ORDER BY thedays.theday
It looks complex, but it is simply the combination of three basic queries. Think of it as a sandwich, with bread and cheese and tomato stuck together with an ORDER BY toothpick.
Here's a more thorough writeup. http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/
MariaDB version 10 has built-in virtual tables of cardinal number sequences like seq_0_to_6. This is convenient.
You need to build a dummy dates table and left join your current table against it.
SELECT dummy.date, SUM(IFNULL(yourtable.record,0)) recordcount
FROM dummy
LEFT JOIN yourtable on dummy.date=yourtable.date
GROUP BT dummy.date
please note that I'm replacing nulls with a zero.
One solution is to create a calendar table containing all the dates you need. You can then left join it to your data to get what you are after
First of all you have to use left join, converting NULLs to 0s using the IFNULL function. Try to match your table and use left join.