I need to find the period without pause in days since last pause.
I have a next table:
id | user | date
-----------------------
1 | 1 | 16.02.2017
1 | 1 | 15.02.2017
1 | 1 | 14.02.2017
1 | 1 | 13.02.2017
1 | 1 | 10.02.2017
Last pause: 10-13 February
Last period without pause: 4 days
I tried to found the difference between day like in this question, but as result it was always NULL. And this is only first part. For second part I thought to use something like ranking, but don't know if it will work.
I plan to use it with PHP 7 + MySQL 5.6.
I've used this sample:
create table if not exists myt(id int, dd date);
insert into myt values
(1, '2017-01-01'),
(1, '2017-01-02'),
(1, '2017-01-03'),
(1, '2017-01-04'),
(1, '2017-01-08'),
(1, '2017-01-09'),
(1, '2017-01-10');
First you should set a partition by consecutive days:
select id, dd,
if(#last_date = '1900-01-01' or datediff(dd, #last_date) = -1, #cn := #cn, #cn := +1) consecutive,
#last_date := dd
from
(select #last_date := '1900-01-01', #cn := 0) x,
(select id, dd
from myt
order by dd desc) y
;
This returns:
+----+---------------------+-------------+
| id | dd | consecutive |
+----+---------------------+-------------+
| 1 | 10.01.2017 00:00:00 | 0 |
| 1 | 09.01.2017 00:00:00 | 0 |
| 1 | 08.01.2017 00:00:00 | 0 |
+----+---------------------+-------------+
| 1 | 04.01.2017 00:00:00 | 1 |
| 1 | 03.01.2017 00:00:00 | 1 |
| 1 | 02.01.2017 00:00:00 | 1 |
| 1 | 01.01.2017 00:00:00 | 1 |
+----+---------------------+-------------+
After you set a partition, then get MAX and MIN date for each partition:
select id, min(dd) as ini, max(dd) as fin, datediff(max(dd), min(dd)) as Days
from (
select id, dd,
if(#last_date = '1900-01-01' or datediff(dd, #last_date) = -1, #cn := #cn, #cn := +1) consecutive,
#last_date := dd
from
(select #last_date := '1900-01-01', #cn := 0) x,
(select id, dd
from myt
order by dd desc) y
) z
group by consecutive
;
Result:
+----+---------------------+---------------------+------+
| id | ini | fin | Days |
+----+---------------------+---------------------+------+
| 1 | 08.01.2017 00:00:00 | 10.01.2017 00:00:00 | 2 |
+----+---------------------+---------------------+------+
| 1 | 01.01.2017 00:00:00 | 04.01.2017 00:00:00 | 3 |
+----+---------------------+---------------------+------+
Check it: http://rextester.com/XMIX80360
Try this query. It will find all pauses -
SELECT curr_date, prev_date FROM (
SELECT t1.date curr_date, MAX(t2.date) prev_date FROM periods t1
LEFT JOIN periods t2
ON t1.date > t2.date
GROUP BY t1.date) t
WHERE DATEDIFF(curr_date, prev_date) > 1
The result is:
13-Feb-17 10-Feb-17
Then add condition/LIMIT to get only one row.
I am trying to write general query like,
select count(distinct t1.`date`) from period t1 left join period t2 ON t2.user = t1.user and t1.`date` - INTERVAL 1 DAY = t2.date where t2.id is null
Can you try this query once,
it should work, I have used it in my almost same case.
This query will return the latest hole:
select
m.`date`,
min(m1.`date`) as next_date,
datediff(min(m1.`date`), m.`date`)+1 as diff
from
mytable m left join mytable m1
on m.`date`<m1.`date`
group by
m.`date`
having
datediff(min(m1.`date`), m.`date`)>1
order by
m.`date` desc
limit 1
The following query:
SELECT MIN(`date`) AS date_start,
MAX(`date`) AS date_end,
MAX(days_diff) AS pause_days,
COUNT(*) + 1 AS period_without_pays
FROM (
SELECT id, user, `date`,
DATE_SUB(`date`, INTERVAL rn DAY) AS group_date,
DATEDIFF(`date`, COALESCE(prevDate, `date`)) AS days_diff
FROM (
SELECT t1.id, t1.user, t1.`date`,
#rn := #rn + 1 AS rn,
(SELECT t2.`date`
FROM mytable AS t2
WHERE t1.id = t2.id AND t1.user = t2.user AND t1.`date` > t2.`date`
ORDER BY `date` DESC LIMIT 1) AS prevDate
FROM mytable AS t1
CROSS JOIN (SELECT #rn := 0) AS v
ORDER BY `date`) AS t) AS x
GROUP BY id, user, group_date, days_diff
HAVING SUM(days_diff) > 0
returns:
date_start date_end pause_days period_without_pays
-----------------------------------------------------
2017-02-14 2017-02-16 1 4
2017-02-13 2017-02-13 3 2
The row with pause_days > 1 returns the start date of the pause along with the number of days.
The row with pause_days = 1 returns the start / end dates of an island of consecutive records having consecutive dates along with the count of these dates.
Note: The above query works with the sample data provided. You may have to tweak the query a little bit so as to adjust it to the complexity of the actual data.
Try this one:
SET #dateDiff=NULL;SET #dateDiff2='';
SELECT diff.secondDate AS fromDate,diff.initialDate AS toDate, diff.dateDiffR FROM (
SELECT d.date AS initialDate,#dateDiff AS secondDate,IF(#dateDiff IS NULL,#dateDiff:=d.date,0) AS try,
IF(DATE(#dateDiff)!=DATE(d.date),DATEDIFF(d.date,#dateDiff),NULL) AS dateDiffR,
IF(#dateDiff!=#dateDiff2,#dateDiff2:=#dateDiff,0) AS try1,
IF(DATE(#dateDiff)!=DATE(d.date),#dateDiff:=d.date,NULL) AS assign FROM
(SELECT b.date FROM mytable b)d ) diff WHERE diff.dateDiffR>0
it will give you the date difference with its date range. If you get negative count then swap the dates on parameters' for DATEDIFF;
(Posted on behalf of the OP).
I adapted little bit query from #McNets:
select user, min(dd) as ini, max(dd) as fin, datediff(max(dd), min(dd))+1 as Days, consecutive
from (
select user,dd,
if(#last_date = curdate() or datediff(dd, #last_date) >= -1, #cn := #cn, #cn := #cn+1) consecutive,
#last_date := dd
from
(select #last_date := curdate(), #cn := 0) x,
(select user, date as dd
from myt
where user = %id
order by dd desc) y
) z
group by consecutive
order by CAST(consecutive AS UNSIGNED)
I added filter by user, changed cause to '>= -1' to accept time usage, added number of the serie and changed initial date from '1900-01-01' to CURDATE() function (i don't see any influence to query result from this action).
Now using number of the series can find the longest series and its dates.
Related
This is sample data in my table
id_item | qty | t_in | t_out | created_at
1 5 1 0 2018-07-05 10:41:00
1 5 1 0 2018-08-03 10:41:00
1 5 0 1 2018-08-05 10:41:00
1 5 1 0 2018-09-05 10:41:00
1 5 1 0 2018-09-20 10:41:00
1 5 0 1 2018-10-31 10:41:00
My expected result will be
id_item | qty | year | month
1 5 2018 07
1 5 2018 08
1 15 2018 09
1 10 2018 10
What i have tried it works, but not desired output when want to group by montly
$date = '2018-10-31'
$test = Model::whereDate('created_at','<=',$date)->select(DB::raw('(SUM(CASE T_IN WHEN 1 THEN qty ELSE qty * - 1 END)) as total'))->groupBy('id_item')->get();
Raw queries to get the quantity for one month
Select id_item,
(SUM(CASE T_IN WHEN 1 THEN qty ELSE qty * - 1 END)) as total
from transactions
where DATE(created_at) <= 2018-10-31
group by id_item
Worst case
$last_day_of_month = [//list of last day of each month]
//then using loop to get qty of each month refer to the raw queries above
From the query above, i only able to get one line of record. I also tried to group by month and year but incorrect result caused of the date condition. How can i include multiple <= $date condition and group it accordingly to get desired output?
Any idea or is that possible to make it? Thanks.
It is a Rolling Sum problem. In newer versions of Mariadb/MySQL, it can be solved using Window Functions with Frames. However, you don't have that available.
We can rather solve this using user-defined variables. In a Derived table, we first determine the total change in qty for a month. Then, we use this result-set to calculate "final qty" at the end of a month, by adding up the previous month (row)'s qty with current month (row)'s qty_change.
I have also extended the query to consider the cases when there are more than one id_item values.
Try the following Raw query:
SELECT
#roll_qty := CASE WHEN #id_itm = dt.id_item
THEN #roll_qty + dt.qty_change
ELSE dt.qty_change
END AS qty,
#id_itm := dt.id_item AS id_item,
dt.year,
dt.month
FROM
(
SELECT
t.id_item,
SUM(t.qty * t.t_in - t.qty * t.t_out) AS qty_change,
YEAR(t.created_at) AS `year`,
LPAD(MONTH(t.created_at), 2, '0') AS `month`
FROM your_table AS t
GROUP BY t.id_item, `year`, `month`
ORDER BY t.id_item, `year`, `month`
) AS dt
CROSS JOIN (SELECT #roll_qty := 0,
#id_itm := 0
) AS user_init_vars;
| id_item | year | month | qty |
| ------- | ---- | ----- | --- |
| 1 | 2018 | 07 | 5 |
| 1 | 2018 | 08 | 5 |
| 1 | 2018 | 09 | 15 |
| 1 | 2018 | 10 | 10 |
View on DB Fiddle
If you are going to use variables, you need to do it correctly. MySQL does not guarantee the order of evaluation of expressions in a SELECT. So, a variable should not be assigned in one expression and then used in another.
This makes for complicated expressions, but it is possible:
select yyyy, mm, total,
(#t := if(#ym = concat_ws('-', yyyy, mm), #t + total,
#ym := concat_ws('-', yyyy, mm), total
)
) as running_total
from (select year(created_at) as yyyy, month(created_at) as mm,
id_item,
sum(case T_IN when 1 then qty else - qty end) as total
from transactions
where created_at < '2018-11-01'
group by id_item
order by id_item, min(created_at)
) i cross join
(select #ym := '', #n := 0);
I need advice.
I have a simple while loop.
And I have a table contestants. Each contestant has a total column [celkem].
These data excerpting the cycle while
And I need that when you have two points as well, so it was always in range (see picture)
The rest of excerpting something like this: $ row ['name']; $ row ['bodycelkem']
My question is, how to use PHP and MySQL dump this range order?
Edit:
SQLFiddler
(I need auto rank where point in range)
Because the Poradi info does not exist, you need to extrapolate it. You can do this with variables. See my SQL below or in the SQLFiddle
SELECT `range`, jmeno, rangeData.celkem FROM `hraci`
LEFT JOIN (
SELECT
a1.*,
if( range_start = range_end,
range_start, CONCAT(range_start,", - ",range_end)
) `range`
FROM (
SELECT
q1.*,
(#runtot + 1) AS range_start,
(#runtot := #runtot + q1.num) range_end
FROM (
SELECT #rn:=#rn+1 rank, t1.num, t1.celkem FROM (
SELECT celkem, count(celkem) num FROM hraci GROUP BY celkem ORDER BY celkem DESC
) t1, (SELECT #rn:=0) t2
) q1, (SELECT #runtot:=0) q2
) a1
) rangeData ON hraci.`celkem` = rangeData.`celkem`
How does this work?
Group the 'celkem' values together, descending. Keep an aggregate of how many 'celkem' values are in each group.
Add a row count to that data, using variables
Using variables again, keep a running total of the group sizes, which will give us the 'range end'. Taking the variable from the last run before incrementing it gives us the 'range start'
If the start and end is the same, just use the start. If the start and end is different, display both values
We now have the Poradi (range?). We can now proceed to just collect our data as normal, but now we can join the groups onto the names.
Neaten all the data up by just selecting the columns we want.
Here's one idea
DROP TABLE IF EXISTS hraci;
CREATE TABLE hraci (
jmeno VARCHAR(45) NULL,
celkem DOUBLE NULL)
ENGINE = InnoDB;
INSERT INTO hraci
(jmeno, celkem) VALUES
('Dan', 97.5),
('Adam', 97.2),
('Petr', 90.5),
('Pavel', 90.5),
('Michal', 87.3),
('Jan', 87.3),
('David', 87.3),
('Tomás', 87.3),
('Jarda', 85.2);
SELECT a.celkem
, a.jmeno
, CASE WHEN MIN(b.rank)-1 <> a.rank THEN CONCAT(a.rank,' - ',MIN(b.rank) - 1) ELSE a.rank END rank
FROM
( SELECT celkem
, jmeno
, FIND_IN_SET(celkem,celkems) rank
FROM hraci
CROSS
JOIN
( SELECT GROUP_CONCAT( celkem ORDER BY celkem DESC) celkems
FROM hraci
) x
) a
LEFT JOIN
( SELECT celkem
, jmeno
, FIND_IN_SET(celkem,celkems) rank
FROM hraci
CROSS
JOIN
( SELECT GROUP_CONCAT( celkem ORDER BY celkem DESC) celkems
FROM hraci
) y
) b
ON b.rank > a.rank
GROUP BY a.celkem,a.jmeno;
+--------+--------+-------+
| celkem | jmeno | rank |
+--------+--------+-------+
| 85.2 | Jarda | 9 |
| 87.3 | David | 5 - 8 |
| 87.3 | Jan | 5 - 8 |
| 87.3 | Michal | 5 - 8 |
| 87.3 | Tomás | 5 - 8 |
| 90.5 | Pavel | 3 - 4 |
| 90.5 | Petr | 3 - 4 |
| 97.2 | Adam | 2 |
| 97.5 | Dan | 1 |
+--------+--------+-------+
http://sqlfiddle.com/#!9/2a7a5/2
I have a simple table which holds the date and the total sales made for a certain group:
date | totalsales
=======================
2014-05-01 | 3000
2014-05-02 | 3100
2014-05-03 | 3500
2014-05-04 | 3650
I like to calculate some things like:
sales per day
average sales
growth in %
Result should look like (calculate by hand so maybe wrong :) )
date | sales | average | growth
=======================================
2014-05-01 | 0 | 0 | 0
2014-05-02 | 100 | 50 | 100
2014-05-03 | 400 | 166.66 | 400
2014-05-04 | 150 | 162.5 | 37.5
Is this even possible in a sql statement or should I calculate with PHP or another server software?
Assuming each date gets its own unique row, you could do it by joining back to your original table like so:
SELECT t1.Date, CASE WHEN t2.Date IS NULL THEN 0 ELSE (t1.totalsales - t2.totalsales)
END AS sales
FROM table t1
LEFT JOIN table t2 ON t2.Date = DATE_ADD(t1.Date, INTERVAL -1 DAY)
ORDER BY 1
This will give you at least your first column, and you should be able to figure out the math for the rest from there. It's important to use a left join with the CASE statement here because otherwise you won't get the lowest date in your table (your first row)
If each date does not get its own unique row, this is method is still viable, you just need to create your datasets in a subquery using GROUP BY and SUM on the date column.
Here is the full query with no subselect at each row : (Thanks to #nmarsh for writting the hardest part)
See SQL fiddle : http://sqlfiddle.com/#!2/be4654/34/0
SELECT
t1.Date,
CASE
WHEN t2.date IS NULL THEN 0 ELSE (t1.totalSales - t2.totalSales)
END AS sales,
CASE
WHEN t2.date IS NULL THEN 0 / (#curRow := #curRow + 1) ELSE ((#curSum := #curSum + (t1.totalSales - t2.totalSales)) / (#curRow := #curRow + 1))
END AS average,
CASE
WHEN t3.date IS NULL AND t2.date IS NULL THEN 0
WHEN t3.date IS NULL THEN (t1.totalSales - t2.totalSales)
WHEN t2.date IS NULL THEN 0 ELSE ((t1.totalSales - t2.totalSales) * 100) / (t2.totalSales - t3.totalSales)
END AS growth
FROM test t1
LEFT JOIN test t2 ON t2.date = DATE_ADD(t1.Date, INTERVAL -1 DAY)
LEFT JOIN test t3 ON t3.date = DATE_ADD(t2.Date, INTERVAL -1 DAY)
JOIN (SELECT #curRow := 0) r
JOIN (SELECT #curSum := 0) ct
ORDER BY 1;
Original table :
date | totalsales
=======================
2014-05-01 | 3000
2014-05-02 | 3100
2014-05-03 | 3500
2014-05-04 | 3650
OUTPUT
date | sales | average | growth
=======================================
2014-05-01 | 0 | 0 | 0
2014-05-02 | 100 | 50 | 100
2014-05-03 | 400 | 166.66 | 400
2014-05-04 | 150 | 162.5 | 37.5
You can use recursive statement. In each iteration, calculate the requested data for one day, and drop the first (oldest) day.
You can also do it with PHP, which seems better because you don't want to put too much load on the MySQL tables in case it does not saves you time/calculations.
Not too sure about the numbers, but if you are more specific about the results I can double check.
You can use ROW_NUMBER() to create 2 data sets and join them on ROW_NUMBER() and ROW_NUMBER()-1 to get an offset to current and previous values to calculate the growth. Sample :
DECLARE #Data TABLE (SalesDate DATETIME, totalSales INT)
INSERT INTO #Data (SalesDate , totalSales) VALUES ('2014-05-01' , 3000)
INSERT INTO #Data (SalesDate , totalSales) VALUES ('2014-05-02' , 3100)
INSERT INTO #Data (SalesDate , totalSales) VALUES ('2014-05-03' , 3500)
INSERT INTO #Data (SalesDate , totalSales) VALUES ('2014-05-04' , 3650)
SELECT
CurrentDt.SalesDate
,ISNULL(CurrentDt.totalSales - PreviousDt.totalSales ,0) AS Sales
,FirstDate.FirstDate
, NULLIF(CAST((CurrentDt.SalesDate - FirstDate.FirstDate) AS INT)+1,0) AS SellingDays
,(ISNULL(CurrentDt.totalSales - PreviousDt.totalSales ,0))
/ NULLIF(CAST((CurrentDt.SalesDate - FirstDate.FirstDate) AS INT)+1,0) AS AverageSales
FROM
(SELECT Min(SalesDate) AS FirstDate FROM #Data) AS FirstDate,
/*Base Sales Data*/
(
SELECT
ROW_NUMBER() OVER(ORDER BY SalesDate) AS RowNum
,SalesDate
,totalSales
FROM
#Data
) AS CurrentDt
/*Previous Value for Growth*/
LEFT JOIN
(
SELECT
ROW_NUMBER() OVER(ORDER BY SalesDate) AS RowNum
,SalesDate
,totalSales
FROM
#Data
) AS PreviousDt
ON CurrentDt.RowNum -1 = PreviousDt.RowNum
I have used MSSQL, but MySQL supports ROW_NUMBER OVER.
I hope this query help you
SELECT
sample.id,
sample.date AS oggi,
sample.value AS sales,
((SELECT SUM(sample.value) FROM sample WHERE sample.date <= oggi ) / (SELECT COUNT(sample.value) FROM sample WHERE sample.date <= oggi ) ) AS avarege,
sample.value / IF((SELECT sample.value FROM sample WHERE sample.date = (oggi - INTERVAL 1 DAY )) = 0,sample.value,(SELECT sample.value FROM sample WHERE sample.date = (oggi - INTERVAL 1 DAY ))) *100 AS 'growt-percent'
-- (SELECT SUM(sample.value) FROM sample WHERE sample.date <= oggi ) AS somma,
-- (SELECT count(sample.value) FROM sample WHERE sample.date <= oggi ) AS conta,
-- (SELECT sample.value FROM sample WHERE sample.date = (oggi - INTERVAL 1 DAY )) as valoreieri,
FROM sample
WHERE sample.date BETWEEN '2014-05-01 00:00:00' AND '2014-05-31 00:00:00'
table data is
id date value
1 2014-05-01 00:00:00 0
2 2014-05-02 00:00:00 100
3 2014-05-03 00:00:00 400
4 2014-05-04 00:00:00 150
5 2014-05-05 00:00:00 200
result is
id oggi sales avarege growt-percent
1 2014-05-01 00:00:00 0 0.0000 (NULL)
2 2014-05-02 00:00:00 100 50.0000 100.0000
3 2014-05-03 00:00:00 400 166.6667 400.0000
4 2014-05-04 00:00:00 150 162.5000 37.5000
5 2014-05-05 00:00:00 200 170.0000 133.3333
note that i use datetime field not only date
if you have question about query ask
sorry for my bad english
edit
the last 3 rows are commented because i used it only for test
Today I am facing a challenge for me, that I could solve with multiple queries, a little bit of PHP and some other funny things, but I was wondering whether what I mean to do can be achieved with a single query and/or stored fn/procedure.
I explain myself better:
in a list of cities, I need to pick up a value (say "general expenses") of that named city (say "Rome").
Pretty simple.
What I would like to do is:
Have 6 records for the same value BEFORE and 6 AFTER the Rome one.
So I would see something:
| position | city | expenses |
| 35 | Paris | 1364775 |
| 36 | Milan | 1378499 |
| 37 | New York | 1385759 |
| 38 | London | 1398594 |
| 39 | Oslo | 1404648 |
| 40 | Munchen | 1414857 |
| 41 | Rome | 1425773 | *** <--this is the value I need
| 42 | Dublin | 1437588 |
| 43 | Athen | 1447758 |
| 44 | Stockholm | 1458593 |
| 46 | Helsinki | 1467489 |
| 47 | Moscow | 1477484 |
| 48 | Kiev | 1485665 |
These values will populate a bars chart.
As you can see there is also another complexity level: the position.
Position must be calculated on all the records.
So let's say I have 100 records, I will have the ranking position from 1 to 100, but only the "limited 13" records must be output.
Any link, suggestion, tutorial or else the could help me out with that?
Thank you in advance as always.
EDIT
Position MUST BE calculated. It is not an input value.
Anyway, thanks folks for all your efforts.
SELECT
all_ranked.*
FROM (select rank
from (SELECT a.id AS id2,
#curRow := #curRow + 1 AS Rank
FROM the_table a
JOIN
(SELECT #curRow := 0) r
ORDER BY position DESC
) AS B)
where B.id=1234567) as rank_record, <--- just one record - value of rank
(SELECT a.id AS id2,
#curRow := #curRow + 1 AS Rank
FROM the_table a
JOIN
(SELECT #curRow := 0) r
ORDER BY position DESC
) AS all_ranked <--- all ranked users
where all_ranked.rank>=rank_record.rank-6 and all_ranked.rank>=rank_record.rank+6
Create 2 queries joined in one. The first gets position and the second sets positions and cut's desired fragment
You could use a stored function/procedure that takes in an input that indicates the subject record, e.g., "Rome" to derive a ranking, which I shall refer to here as the perceived ID (PID) for that record, e.g., 41. You can then use a variable #PID to store that location.
Then you can do your ranking query again but select all records.
SELECT .... WHERE Ranking BETWEEN (#PID-6) AND (#PID+6)
An advantage to doing it this way is that the function/procedure can take in an additional parameter to allow it to fetch X records after and Y records before that ranking. It would be easier to read and maintain as well.
Performing it as a single query without the use of PHP would be tricky as you need to insert a WHERE clause in which the condition is the result of another query.
If your position is going to be continuous unique number you can use sub-query in where condition.
SELECT `position`, `city`, `expenses`
FROM table_name
WHERE `position` > (
SELECT `position`-7
FROM table_name
WHERE `city`='Rome'
)
ORDER BY `position`
LIMIT 13
PS: I am not an expert in SQL. There may be better more efficient ways.
I haven't tried it, but this should work: You get the positions with a variable you increment while selecting in MySQL. Then you would have to select this "temporary table" twice; once to find Rome, once to find all 13 records:
select
from
(
select #rowno := #rowno + 1 as position, city, expenses
from cities
cross join (select #rowno := 0)
order by expenses
)
where abs(position -
(
select position, city
from
(
select #rowno := #rowno + 1 as position, city, expenses
from cities
cross join (select #rowno := 0)
order by expenses
)
where city = 'Rome'
)
) <= 6;
SELECT rank as position,city,expenses FROM
(SELECT #rownum := #rownum + 1 AS position, city, expenses, FIND_IN_SET( position, (
SELECT GROUP_CONCAT( position
ORDER BY position ASC )
FROM test )
) AS rank
FROM test,(SELECT #rownum := 0) r
HAVING rank BETWEEN(SELECT FIND_IN_SET( position, (
SELECT GROUP_CONCAT( position
ORDER BY position ASC )
FROM test )
)-6 AS rank
FROM test
WHERE expenses=1425773)
AND
(SELECT FIND_IN_SET( position, (
SELECT GROUP_CONCAT( position
ORDER BY position ASC )
FROM test )
)+6 AS rank
FROM test
WHERE expenses=1425773))x
FIDDLE
My Db design is :
Tbl_id booked_by date
1 W 2014-02-01
2 P 2014-02-01
3 P 2014-02-01
4 P 2014-02-01
5 W 2014-02-04
6 W 2014-02-04
7 P 2014-02-04
I want to display counting of W , P Where given Date and Grouped by Date and with Using php mysql ..
like
I NEED OUTPUT LIKE THIS:
Tbl_id W P date
1 1 3 2014-02-01
2 2 1 2014-02-04
I need Mysql Query to get output given in above..
You need to use SUM aggregate function for summation and a temp row number for Tbl_id.
select
#rwnm:=#rwnm+1 as Tbl_id,
sum(No_people) as No_people,
sum(Breakfast) as Breakfast,
sum(Lunch) as Lunch,
date
from my_table, ( select #rwnm := 0 ) rownums
group by date
You need the SUM() function, not the COUNT().
SELECT
SUM(No_people),
SUM(Breakfast),
SUM(Lunch),
`date`
FROM yourTable
GROUP BY `date`
I did not include the Tbl_id, because every column you select must be used with an aggregate function or be included in the group by clause. Otherwise it's undetermined which row of the group is selected. Selecting it anyway only works in MySQL, but it does not adhere to the SQL standard. If you want some kind of row number instead either Ravinder's approach or create one on application level (which certainly is easier).
UPDATE:
SELECT
SUM(booked_by = 'W') AS W,
SUM(booked_by = 'P') AS P,
`date`
FROM yourTable
GROUP BY `date`
You still need the SUM() function. booked_by = 'whatever' evaluates to true (1) or false (0).
If you want just the result without slno below query will work fine
SELECT
SUM(No_People) as No_People,
SUM(Breakfast) as Breakfast,
Sum(Lunch) as Lunch,
`date` as date
FROM table_Name
GROUP BY `date`
Output:
No_people Breakfast Lunch date
84 112 160 2014-02-11
116 96 118 2014-02-04
But if you also want serial no. to be displayed then below query will work
SELECT
#slno := #slno + 1 as slno
SUM(No_People) as No_People,
SUM(Breakfast) as Breakfast,
Sum(Lunch) as Lunch,
`date` as date
FROM table_Name, (select #slno := 0 ) sln
GROUP BY `date`
Output:
slno No_people Breakfast Lunch date
1 84 112 160 2014-02-11
2 116 96 118 2014-02-04
Consider the following data set...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,booked_by CHAR(1) NOT NULL
,date DATE NOT NULL
);
INSERT INTO my_table VALUES
(1,'W','2014-02-01'),
(2,'P','2014-02-01'),
(3,'P','2014-02-01'),
(4,'P','2014-02-01'),
(5,'W','2014-02-04'),
(6,'W','2014-02-04'),
(7,'P','2014-02-04');
Here's a pivot query...
SELECT date
, COUNT(CASE WHEN booked_by = 'W' THEN 'foo' END) W
, COUNT(CASE WHEN booked_by = 'P' THEN 'foo' END) P
FROM my_table
GROUP
BY date;
+------------+---+---+
| date | W | P |
+------------+---+---+
| 2014-02-01 | 1 | 3 |
| 2014-02-04 | 2 | 1 |
+------------+---+---+
In my opinion, a better idea (more flexible, more scalable) is simply to return a GROUPED (and, therefore, ordered) result set and handle the rest at in the presentation layer (e.g. a simple PHP loop)...
SELECT date,booked_by,COUNT(*) ttl FROM my_table GROUP BY date,booked_by;
+------------+-----------+-----+
| date | booked_by | ttl |
+------------+-----------+-----+
| 2014-02-01 | P | 3 |
| 2014-02-01 | W | 1 |
| 2014-02-04 | P | 1 |
| 2014-02-04 | W | 2 |
+------------+-----------+-----+