I can't get my query to work when a LEFT join doesn't match. I think this is related to the cartesian join. Here's a query that works:
select i.id, z.WeekNum, 0, count(s.id) from my156rowtable as i
left JOIN (select YEARWEEK( #startDate ) WeekNum, #startDate := date_add( #startDate, interval 1 week ) EndOfWeek
from
(select #startDate := '2016-04-20') sqlv,
my156rowtable
limit 156 ) z
on 1=1
where i.id = 2 and s.id = 0
group by z.WeekNum, s.id
Which brings back:
| 2 | 201836 | 0 | 1 |
| 2 | 201837 | 0 | 1 |
| 2 | 201838 | 0 | 1 |
| 2 | 201839 | 0 | 1 |
| 2 | 201840 | 0 | 1 |
| 2 | 201841 | 0 | 1 |
but with my addition of stats_to_my156rowtable I get rows 201838 and 201839 missing.
select i.id, z.WeekNum, 0, count(s.id) from my156rowtable as i
left JOIN (select YEARWEEK( #startDate ) WeekNum, #startDate := date_add( #startDate, interval 1 week ) EndOfWeek
from
(select #startDate := '2016-04-20') sqlv,
my156rowtable
limit 156 ) z
on 1=1
left join stats_to_my156rowtable as s
on i.id = s.sid and z.WeekNum = YearWeek(s.date)
where i.id = 2 and s.id = 0
group by z.WeekNum, s.id
returns:
| 2 | 201836 | 0 | 8 |
| 2 | 201837 | 0 | 14 |
| 2 | 201840 | 0 | 9 |
if there is no match I'd like the count to be 0. I figured I could this with a case or coalesce later but with no return I'm not finding much luck.
Your condition on the right table in the WHERE clause turn your LEFT JOIN into INNER JOIN. Basically, it filter out rows that doesn't have id = 0, including your NULL rows. Probably, moving the condition to the ON clause will solve your problem.
select i.id, z.WeekNum, 0, count(s.id)
from my156rowtable as i
left JOIN (
select YEARWEEK( #startDate ) WeekNum, #startDate := date_add( #startDate, interval 1 week ) EndOfWeek
from (
select #startDate := '2016-04-20'
) sqlv, my156rowtable
limit 156
) z on 1=1
left join stats_to_my156rowtable as s on i.id = s.sid and z.WeekNum = YearWeek(s.date) AND s.id = 0
where i.id = 2
group by z.WeekNum, s.id
Related
Here my table structure:
___Lang:
|--------|------------|
| LAN_Id | LAN_En |
|--------|------------|
| DI | Direct |
| WE | Web |
| OT | Other |
|--------|------------|
___Segmentations:
|--------|------------|
| SEG_Id | SEG_Code |
|--------|------------|
| 1 | DI |
| 2 | WE |
| 3 | OT |
|--------|------------|
___Bookings:
|--------|------------------|
| BOO_Id | BOO_Segmentation |
|--------|------------------|
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
|--------|------------------|
___BillableDatas:
|--------|---------------|------------|------------|
| BIL_Id | BIL_BookingId | BIL_Date | BIL_Item |
|--------|---------------|------------|------------|
| 1 | 1 | 2017-02-21 | Night |
| 2 | 1 | 2017-02-22 | Night |
| 3 | 1 | 2017-02-23 | Night |
| 4 | 1 | 2017-02-24 | Night |
| 5 | 2 | 2017-02-25 | Night |
| 6 | 2 | 2017-02-26 | Night |
| 7 | 3 | 2017-02-28 | Night |
| 8 | 3 | 2017-03-01 | Night |
| 9 | 3 | 2017-03-02 | Night |
| 10 | 3 | 2017-03-03 | Night |
|--------|---------------|------------|------------|
I would like to know the most popular segmentation for a range of date.
The desired result should be this one for the following date range :
Form 2017-02-01 to 2017-02-28 inclusive
|------------|------------|------------|--------------|------------|
| ROO_Name | Night_Nb | Percentage | Booking_Nb | Percentage |
|------------|------------|------------|--------------|------------|
| Direct | 6 | 85.71 | 2 | 66.66 |
| Website | 1 | 14.28 | 1 | 33.33 |
| Other | 0 | 0 | 0 | 0 |
|------------|------------|------------|--------------|------------|
What I already tried:
SELECT r.SEG_Id
, Sum(CASE WHEN BOO_Id IS NULL THEN 0 ELSE 1 END) Night_Nb
, Concat(
Format(
Sum(CASE WHEN BOO_Id IS NULL THEN 0 ELSE 1 END)
/ TotalBookings
* 100
, 0) ) AS PercentageTotal
FROM ( ___Segmentations r LEFT JOIN ___Bookings b ON r.SEG_Id = b.BOO_Segmentation
) INNER JOIN (SELECT BOO_HotelId
, Count(*) AS TotalBookings
FROM ___Bookings
GROUP BY BOO_HotelId
) AS TotalHotelBookings
ON r.SEG_HotelId = TotalHotelBookings.BOO_HotelId
WHERE r.SEG_HotelId = :hotel_id
GROUP BY r.SEG_Id
ORDER BY NumBookings DESC
But it doesn't work actually.
Could anyone help me with this please ?
You could use the SQL Fiddle:
http://sqlfiddle.com/#!9/1aa10a
I suggest we build the query incrementally, step by step. Verify that the query results are as we expect at each step. When something "doesn't work", backup a step.
We want to return three rows, one for each row in ___Segmentations, for a specific hotelid
SELECT r.seg_id
, r.seg_text
FROM ___Segmentations r
WHERE r.seg_hotelid = :hotel_id
ORDER BY r.seg_id
Add the outer join to __Bookings
SELECT r.seg_id
, r.seg_text
, b.boo_id
FROM ___Segmentations r
LEFT
JOIN ___Bookings b
ON b.boo_segmentation = r.seg_id
WHERE r.seg_hotelid = :hotel_id
ORDER
BY r.seg_id
, b.boo_id
Add the outer join to ___BillableDatas
SELECT r.seg_id
, r.seg_text
, b.boo_id
, d.bil_id
FROM ___Segmentations r
LEFT
JOIN ___Bookings b
ON b.boo_segmentation = r.seg_id
LEFT
JOIN `___BillableDatas` d
ON d.bil_bookingid = b.boo_id
WHERE r.seg_hotelid = :hotel_id
ORDER
BY r.seg_id
, b.boo_id
, d.bil_id
If that's the rows we're interested in, we can work on aggregation.
SELECT r.seg_id
, r.seg_text
, COUNT(DISTINCT b.boo_id) AS cnt_bookings
, COUNT(DISTINCT d.bil_id) AS cnt_billable
FROM ___Segmentations r
LEFT
JOIN ___Bookings b
ON b.boo_segmentation = r.seg_id
LEFT
JOIN `___BillableDatas` d
ON d.bil_bookingid = b.boo_id
WHERE r.seg_hotelid = :hotel_id
GROUP
BY r.seg_id
, r.seg_text
ORDER
BY r.seg_text
Now to get the aggregation with the "total".
The approach I would take would be to make "copies" of the rows, using a CROSS JOIN operation. We can do the join to the rows returned by the very first query we wrote, referenced as an inline view. (Aliased as q below.)
If we have a complete set of rows, repeated for each seg_id/seg_text (that first query we wrote), we can use conditional aggregation.
That last query we wrote (just above) is an inline view in the query below, aliased as c.
SUM of cnt_bookings from all the rows is the total.
For the individual counts, we can include only the rows that have a matching seg_id, a total of that subset.
SELECT q.seg_id
, q.seg_text
, SUM(IF(c.seg_id=q.seg_id,c.cnt_bookings,0)) AS cnt_bookings
, SUM(c.cnt_bookings) AS tot_bookings
, SUM(IF(c.seg_id=q.seg_id,c.cnt_billable,0)) AS cnt_billable
, SUM(c.cnt_billable) AS tot_billable
FROM ( SELECT t.seg_id
, t.seg_text
FROM ___Segmentations t
WHERE t.seg_hotelid = :hotel_id_1
ORDER BY t.seg_id
) q
CROSS
JOIN ( SELECT r.seg_id
, COUNT(DISTINCT b.boo_id) AS cnt_bookings
, COUNT(DISTINCT d.bil_id) AS cnt_billable
FROM ___Segmentations r
LEFT
JOIN ___Bookings b
ON b.boo_segmentation = r.seg_id
LEFT
JOIN `___BillableDatas` d
ON d.bil_bookingid = b.boo_id
WHERE r.seg_hotelid = :hotel_id
GROUP
BY r.seg_id
) c
GROUP
BY q.seg_id
, q.seg_text
ORDER
BY q.seg_text
In the SELECT list, we can do the division to get the percentage: cnt_bookings * 100.0 / tot_bookings
e.g.
SELECT q.seg_id
, q.seg_text
, SUM(IF(c.seg_id=q.seg_id,c.cnt_bookings,0)) AS cnt_bookings
, SUM(c.cnt_bookings) AS tot_bookings
, SUM(IF(c.seg_id=q.seg_id,c.cnt_bookings,0))
* 100.0 / SUM(c.cnt_bookings) AS pct_bookings
, SUM(IF(c.seg_id=q.seg_id,c.cnt_billable,0)) AS cnt_billable
, SUM(c.cnt_billable) AS tot_billable
, SUM(IF(c.seg_id=q.seg_id,c.cnt_billable,0))
* 100.0 / SUM(c.cnt_billable) AS pct_billable
Modify the ORDER BY clause to return the rows in the order you want
Remove from the SELECT list the expressions that return tot_bookings and tot_billable.
EDIT
I think I missed the date critera. We can make the outer joins into inner joins, and replace the CROSS JOIN with a LEFT JOIN. We have potential to return NULL values for cnt_bookings and cnt_billable, we can wrap those in IFNULL() or COALESCE() function to replace NULL with zero.
SELECT q.seg_id
, q.seg_text
, SUM(IF(c.seg_id=q.seg_id,c.cnt_bookings,0)) AS cnt_bookings
, SUM(c.cnt_bookings) AS tot_bookings
, SUM(IF(c.seg_id=q.seg_id,c.cnt_bookings,0))
* 100.0 / SUM(c.cnt_bookings) AS pct_bookings
, SUM(IF(c.seg_id=q.seg_id,c.cnt_billable,0)) AS cnt_billable
, SUM(c.cnt_billable) AS tot_billable
, SUM(IF(c.seg_id=q.seg_id,c.cnt_billable,0))
* 100.0 / SUM(c.cnt_billable) AS pct_billable
FROM ( SELECT t.seg_id
, t.seg_text
FROM ___Segmentations t
WHERE t.seg_hotelid = :hotel_id_1
ORDER BY t.seg_id
) q
LEFT
JOIN ( SELECT r.seg_id
, COUNT(DISTINCT b.boo_id) AS cnt_bookings
, COUNT(DISTINCT d.bil_id) AS cnt_billable
FROM ___Segmentations r
JOIN ___Bookings b
ON b.boo_segmentation = r.seg_id
JOIN `___BillableDatas` d
ON d.bil_bookingid = b.boo_id
AND d.bil_date BETWEEN '2017-02-21' AND '2017-02-28'
WHERE r.seg_hotelid = :hotel_id
GROUP
BY r.seg_id
) c
ON 1=1
GROUP
BY q.seg_id
, q.seg_text
ORDER
BY q.seg_text
Not sure 100% how the hotelId column comes into play here, you didn't describe it in the question, but try this:
SELECT aaa.SEG_Text, aaa.Night_NB, aaa.Night_NB / totals.total_nights * 100, aaa.Booking_Nb , aaa.Booking_Nb / totals.total_bookings * 100 FROM (
SELECT s.SEG_Text, COUNT(DISTINCT d.BIL_Id) AS `Night_Nb`, COUNT(DISTINCT b.BOO_Id) AS `Booking_Nb` FROM ___Segmentations s
LEFT JOIN ___Bookings b ON s.SEG_Id = b.BOO_Segmentation
LEFT JOIN ___BillableDatas d ON d.BIL_BookingId = b.BOO_Id AND d.BIL_Date BETWEEN '2017-02-01' AND '2017-02-28'
GROUP BY s.SEG_Id ) AS `aaa`
, ( SELECT COUNT(*) AS `total_nights`, COUNT(DISTINCT BIL_BookingId) `total_bookings` FROM ___BillableDatas WHERE BIL_Date BETWEEN '2017-02-01' AND '2017-02-28') AS totals
It basically does the same stuff you did, but uses SELECT(DISTINCT ...) and thus is easier to understand, debug, and I think will also run faster.
For me it returns correct results.
The challenge here seems to be to avoid doing almost the same query twice (repeating the date condition) in order to calculate the two percentages.
You could use the with rollup modifier to generate the total counts which you need to calculate those percentages. You could then capture these totals in variables, and use them in a wrapping query as divisors. Finally, the outer query's where clause would eliminate the rollup record, as it has served its purpose:
select seg_text
, night_nb
, 100*night_nb/#sum_night_nb as night_pct
, booking_nb
, 100*booking_nb/#sum_booking_nb as booking_pct
from (
select seg_text
, #sum_night_nb := count(bil_id) night_nb
, #sum_booking_nb := count(distinct bil_bookingid) booking_nb
from ___segmentations seg
left join (___bookings boo
inner join ___billabledatas bil
on bil_bookingid = boo_id
and bil_hotelid = boo_hotelid)
on seg_id = boo_segmentation
and seg_hotelid = boo_hotelid
and bil_date between '2017-02-01' and '2017-02-28'
where seg_hotelid = 'AAA00'
group by seg_text with rollup
) base
where seg_text is not null
order by night_nb desc
See this sqlfiddle
SELECT i.id invn_id, IF(ss.serial_id IS NOT NULL, ss.serial_id, NULL) serial_number,
IF(ss.serial_id IS NOT NULL, 1.000000, -(i.qty)) qty,
'auto' flag, i.sid site_id, i.prod_id, i.fifo_total_amount, i.trans_date
FROM inventories i
INNER JOIN prod p ON p.id = i.prod_id
LEFT JOIN sale_products sp ON sp.inventory_id = i.id
LEFT JOIN sale_serial ss ON ss.prod_id = i.prod_id AND ss.sale_id = sp.sale_id
WHERE i.qty < 0
AND ('2015-03-25 00:00:00' IS NULL OR i.trans_date >= '2015-03-25 00:00:00')
AND ('2015-03-27 08:27:36' IS NULL OR i.trans_date <= '2015-03-27 08:27:36')
AND p.name NOT IN ('Starting Balance' , 'Opening Balance', 'Equity')
AND i.prod_id = 7655 AND (0 = 0 OR i.sid = 0)
UNION ALL
(
SELECT i.id invn_id,
IF(ss.serial_id IS NOT NULL,ss.serial_id, NULL) serial_number,
-(SUM(IF(ss.serial_id IS NOT NULL, 1.000000, -(i.qty))) - -(i.qty)) qty, -- difference
'auto' flag, i.sid site_id, i.prod_id, i.fifo_total_amount, i.trans_date
FROM inventories i
INNER JOIN prod p ON p.id = i.prod_id
LEFT JOIN sale_products sp ON sp.inventory_id = i.id
LEFT JOIN sale_serial ss ON ss.prod_id = i.prod_id AND ss.sale_id = sp.sale_id
WHERE i.qty < 0
AND ('2015-03-25 00:00:00' IS NULL OR i.trans_date >= '2015-03-25 00:00:00')
AND ('2015-03-27 08:27:36' IS NULL OR i.trans_date <= '2015-03-27 08:27:36')
AND p.name NOT IN ('Starting Balance' , 'Opening Balance', 'Equity')
AND i.prod_id = 7655 AND (0 = 0 OR i.sid = 0)
GROUP BY i.id -- difference
HAVING qty > 0 -- difference
)
ORDER BY site_id , prod_id , trans_date , qty ASC
Observe that the second statement (union table) is almost the same as the first statement except for the lines in comment. I'm satisfied with the outcome, but not satisfied with the query because of its redundancy. Is there a possibility to make this brief?
The output I wanna get is something like this, for instance:
I have a total quantity of 5 with an ID of 95514:
+-------+-----------+---------+
| id | qty | prod_id |
+-------+-----------+---------+
| 95514 | 5.000000 | 7655 |
+-------+-----------+---------+
If I'll execute the query above, the result will be like this:
+---------+---------------+----------+------+---------+---------+-------------------+---------------------+
| invn_id | serial_number | qty | flag | site_id | prod_id | fifo_total_amount | trans_date |
+---------+---------------+----------+------+---------+---------+-------------------+---------------------+
| 95514 | 237658 | 1.000000 | auto | 1 | 7655 | 2763.0194 | 2010-07-22 09:48:24 |
| 95514 | 237671 | 1.000000 | auto | 1 | 7655 | 2763.0194 | 2010-07-22 09:48:24 |
| 95514 | 237699 | 1.000000 | auto | 1 | 7655 | 2763.0194 | 2010-07-22 09:48:24 |
| 95514 | 237658 | 2.000000 | auto | 1 | 7655 | 2763.0194 | 2010-07-22 09:48:24 |
+---------+---------------+----------+------+---------+---------+-------------------+---------------------+
The first three rows returned the first statement of the query in UNION ALL, while the fourth row returned the second statement. If we'll sum up the qty column, we can get the value of 5.
I suspect you want group by with rollup:
SELECT i.id invn_id,
IF(ss.serial_id IS NOT NULL,ss.serial_id, NULL) serial_number,
-(SUM(IF(ss.serial_id IS NOT NULL, 1.000000, -(i.qty))) - -(i.qty)) qty, -- difference
'auto' flag, i.sid site_id, i.prod_id, i.fifo_total_amount, i.trans_date
FROM inventories i
INNER JOIN prod p ON p.id = i.prod_id
LEFT JOIN sale_products sp ON sp.inventory_id = i.id
LEFT JOIN sale_serial ss ON ss.prod_id = i.prod_id AND ss.sale_id = sp.sale_id
WHERE i.qty < 0
AND ('2015-03-25 00:00:00' IS NULL OR i.trans_date >= '2015-03-25 00:00:00')
AND ('2015-03-27 08:27:36' IS NULL OR i.trans_date <= '2015-03-27 08:27:36')
AND p.name NOT IN ('Starting Balance' , 'Opening Balance', 'Equity')
AND i.prod_id = 7655 AND (0 = 0 OR i.sid = 0)
GROUP BY i.id WITH ROLLUP
HAVING qty > 0 -- difference
However, I'm not 100% sure how this fits into the SELECT logic.
I have a these tables
USERS
+----------+---------------+
| id_users | usr_email |
+----------+---------------+
| 1 | a#domain.com |
| 2 | b#domain.com |
| 3 | c#domain.com |
| 4 | d#domain.com |
| 5 | e#domain.com |
+----------+---------------+
RANKING
+-------------+-----------+----------+
| id_ranking | id_users | points |
+-------------+-----------+----------+
| 50 | 1 | 27 | //3rd
| 51 | 2 | 55 | //1st
| 52 | 3 | 9 | //5th
| 53 | 4 | 14 | //4th
| 54 | 5 | 38 | //2nd
+-------------+-----------+----------+
I would like to retireve user's data along with it's ranking position, filtering by e-mail. So for example if I want info for mail c#domain.com I should get
+----------+--------|---------------+
| id_users | points | rank_position |
+----------+--------|---------------+
| 3 | 9 | 5 |
+----------+--------|---------------+
I've found this piece of query that returns the ranking position
SELECT x.id_users, x.position
FROM (
SELECT t1.id_ranking, t1.id_users, #rownum := #rownum + 1 AS position
FROM ranking t1
JOIN (SELECT #rownum := 0) r ORDER BY t1.points desc
) x
WHERE x.id_users = 3
But I can't manage to use it in my old query
select u.*, r.points
from users u
left join ranking r on r.id_users = u.id_users
where u.usr_email = 'c#domain.com'
My attemp
select u.*, r.points, p.*
from users u
left join ranking r on r.id_users = u.id_users,
(SELECT x.id_users, x.position
FROM (
SELECT t1.id_ranking, t1.id_users, #rownum := #rownum + 1 AS position
FROM ranking t1
JOIN (SELECT #rownum := 0) r ORDER BY t1.points desc
) x
WHERE x.id_users = u.id_users) p
where u.usr_email = 'c#domain.com'
Any help?
You are missing a join condition. But also, the outer join to ranking is not necessary. You can "remember" the points in the subquery:
select u.*, r.points, r.position
from users u left join
(select r.*, #rownum := #rownum + 1 AS position
from ranking r CROSS JOIN
(SELECT #rownum := 0) r
order by r.points desc
) r
ON r.id_users = u.id_users
where u.usr_email = 'c#domain.com'
select u.id_users,
r.points,
count(ifnull(r2.id_users, 0)) + 1 as rank_position
from users u
join ranking r
on u.id_users = r.id_users
left join ranking r2
on r2.points > r.points
where u.usr_email = 'c#domain.com'
group by u.id_users, r.points
Fiddle: http://sqlfiddle.com/#!2/1444e8/1/0
Rather than a variable this counts the number of users who have more points than the given user, and then adds one. This is an equivalent way of calculating their rank, in terms of number of points.
friends,
I would like to create a query to return me 2 lines above and 2 lines below the selected ID, but the comparison must be made by the ranking.
Query that creates a league table.
SET #rowId :=0;
SELECT
#rowid:= #rowid + 1 AS ranking,
tabelaCompleta.*
FROM
(SELECT
tbl_timeCartola.nomeTime AS nomeTime,
SUM( tbl_ponto.ponto ) AS totalPontos,
tbl_timeCartola.FK_loginID
FROM tbl_ponto
INNER JOIN tbl_timeCartola ON tbl_timeCartola.FK_loginID = tbl_ponto.FK_loginID
WHERE tbl_timeCartola.FK_loginID IN ( SELECT FK_loginID FROM tbl_campeonatoUsuario WHERE FK_campeonatoID = '1' )
GROUP BY tbl_timeCartola.nomeTime
ORDER BY totalPontos DESC ) tabelaCompleta;
This query return this:
+---------+-------------------+-------------+------------+
| ranking | nomeTime | totalPontos | FK_loginID |
+---------+-------------------+-------------+------------+
| 1 | Mathemio Greus-SB | 612.90 | 7 |
| 2 | CR Hipotenusa VG | 572.67 | 4 |
| 3 | Zica Danada | 549.20 | 6 |
| 4 | FC LEEDS UNITED | 516.12 | 8 |
| 5 | Bradock F.C | 503.51 | 5 |
+---------+-------------------+-------------+------------+
Try this code. The rankings are derived in two different inline views. Then, they are joined using the ranking (if the second inline view's ranking is -1,=, or +1 than the first inline view's ranking). The data is filtered for the selectedId.
SET #rowId1 :=0;
SET #rowId2 :=0;
SET #selectedId := 8;
SELECT
tabela_ordenada2.*
FROM
(
SELECT
#rowId1 := #rowId1 + 1 AS ranking,
tabelaCompleta.*
FROM
(SELECT
tbl_timeCartola.nomeTime AS nomeTime,
SUM( tbl_ponto.ponto ) AS totalPontos,
tbl_timeCartola.FK_loginID AS FK_loginID
FROM tbl_ponto
INNER JOIN tbl_timeCartola ON tbl_timeCartola.FK_loginID = tbl_ponto.FK_loginID
WHERE tbl_timeCartola.FK_loginID IN ( SELECT FK_loginID FROM tbl_campeonatoUsuario WHERE FK_campeonatoID = '1' )
GROUP BY tbl_timeCartola.nomeTime
ORDER BY totalPontos DESC ) tabelaCompleta
) tabela_ordenada1
INNER JOIN
(
SELECT
#rowId2 := #rowId2 + 1 AS ranking,
tabelaCompleta.*
FROM
(SELECT
tbl_timeCartola.nomeTime AS nomeTime,
SUM( tbl_ponto.ponto ) AS totalPontos,
tbl_timeCartola.FK_loginID AS FK_loginID
FROM tbl_ponto
INNER JOIN tbl_timeCartola ON tbl_timeCartola.FK_loginID = tbl_ponto.FK_loginID
WHERE tbl_timeCartola.FK_loginID IN ( SELECT FK_loginID FROM tbl_campeonatoUsuario WHERE FK_campeonatoID = '1' )
GROUP BY tbl_timeCartola.nomeTime
ORDER BY totalPontos DESC ) tabelaCompleta
) tabela_ordenada2
ON (tabela_ordenada2.ranking = tabela_ordenada1.ranking - 1 OR tabela_ordenada2.ranking = tabela_ordenada1.ranking OR tabela_ordenada2.ranking = tabela_ordenada1.ranking + 1)
WHERE tabela_ordenada1.FK_loginID = #selectedId;
I have two tables
user_raters:
| id(int) | to_id(int) | value(int) | created_at(datetime)
|1 | 2 | 1 | 2009-03-01 00:00:00
EDIT: I changed the user_rater_id. history_user_raters.user_rater_id is related to user_raters.id
history_user_raters:
| id(int) | user_rater_id(int) | value(int) | created_at(datetime)
| 1 | 1 | 1 | 2009-03-02 00:00:00
| 2 | 1 | 1 | 2009-03-02 00:00:00
| 3 | 1 | -1 | 2009-03-02 00:00:00
| 4 | 1 | 1 | 2009-03-03 00:00:00
| 5 | 1 | -1 | 2009-03-03 00:00:00
| 6 | 1 | -1 | 2009-03-03 00:00:00
| 7 | 1 | -1 | 2009-03-03 00:00:00
I want to count the sum of the values from history_user_raters as it relates to the to_id from user_raters. The result from the query should be:
| year | month | day | total | down | up
| 2009 | 3 | 2 | 1 | 1 | 2
| 2009 | 3 | 3 | -2 | 3 | 1
I have a query that is close, but it is not counting the up and down correctly. The total is right. Can some one help me write the query or new query that calculates correct up and down?
My current query:
SELECT
YEAR(history.created_at) AS `year`,
MONTH(history.created_at) AS `month`,
DAY(history.created_at) AS `day`,
SUM(history.value) as `total`,
(SELECT
abs(SUM(historydown.value))
FROM `user_raters` as raterdown
INNER JOIN `history_user_raters` AS historydown
WHERE raterdown.to_id = 2
AND historydown.value = -1
AND date(historydown.created_at)
GROUP BY history.created_at) as down,
(SELECT SUM(historyup.value)
FROM `user_raters` as raterup
INNER JOIN `history_user_raters` AS historyup
WHERE raterup.to_id = 2
AND historyup.value = 1
AND date(history.created_at)
GROUP BY raterup.to_id) as up
FROM `user_raters`
INNER JOIN history_user_raters AS history ON user_raters.id = history.user_rater_id
WHERE (user_raters.to_id = 2)
GROUP BY DATE(history.created_at)
I might see it too simply (and sorry I can't test with data at the moment), but I'm guessing the following trick with two CASE statements would do just what is needed
SELECT
YEAR(history.created_at) AS year,
MONTH(history.created_at) AS month,
DAY(history.created_at) AS day,
SUM(history.value) as total,
SUM(CASE WHEN history.value < 0 THEN history.value ELSE 0 END) as down,
SUM(CASE WHEN history.value > 0 THEN history.value ELSE 0 END) as up
FROM `user_raters`
INNER JOIN `history_user_raters` AS history
ON user_raters.id = history.user_rater_id
WHERE (user_raters.to_id = 1) -- or some other condition...
GROUP BY DATE(history.created_at)
EDIT: #OMG Ponies deleted his answer. This response make no sense now, but I am not going to delete my answer, because I think it is silly.
#OMG ponies
Your query runs, but it returns no results. I had to adjust it a bit to add the to_id in the main queries where clause
SELECT
YEAR( t.created_at ) AS `year` ,
MONTH( t.created_at ) AS `month` ,
DAY( t.created_at ) AS `day` ,
SUM( t.value ) AS `total` ,
MAX( COALESCE( x.sum_down, 0 ) ) AS down,
MAX( COALESCE( y.sum_up, 0 ) ) AS up
FROM history_user_raters AS t
JOIN user_raters AS ur ON ur.to_id = t.user_rater_id
LEFT JOIN (
SELECT hur.user_rater_id,
SUM( hur.value ) AS sum_down
FROM history_user_raters AS hur
WHERE hur.value = -1
GROUP BY hur.user_rater_id
) AS x ON x.user_rater_id = t.user_rater_id
LEFT JOIN (
SELECT hur.user_rater_id,
SUM( hur.value ) AS sum_up
FROM history_user_raters AS hur
WHERE hur.value =1
GROUP BY hur.user_rater_id
) AS y ON y.user_rater_id = t.user_rater_id
WHERE ur.to_id =1
GROUP BY YEAR( t.created_at ) , MONTH( t.created_at ) , DAY( t.created_at )