Mysql Check whether customer bought monthly - mysql

this is a sample of my table. (Actual table contains much more rows)
--------------------------------------------------
fInvoice | fDebtorcode | fCompanyname | fdate
123 | A001 | ABC Company | 2017-02-01
234 | A002 | CDE Company | 2017-03-02
345 | A003 | FGH Company | 2017-03-05
456 | A001 | ABC Company | 2017-04-02
567 | A001 | ABC Company | 2017-04-04
678 | A002 | CDE Company | 2017-04-04
I'd like to find out how many companies bought from us monthly. here's what I have so far:
select
fdebtorcode As Debtorcode,
fcompanyname As CompanyName,
(SELECT COUNT(*) FROM invoicetable WHERE fdate > '2017-03-31' and fdate < '2017-05-01' and fdebtorcode = Debtorcode) As April,
(SELECT COUNT(*) FROM invoicetable WHERE fdate > '2017-02-28' and fdate < '2017-04-01' and fdebtorcode = Debtorcode) As March,
(SELECT COUNT(*) FROM invoicetable WHERE fdate > '2017-01-31' and fdate < '2017-03-01' and fdebtorcode = Debtorcode) As February
from tinvoicehistory
where fdivision = 'Stationary'
group by fdebtorcode
order by fdebtorcode
Now, I've got 2 more requirements:
a column called "Purchased Monthly" which gives me a "yes" for companies which bought monthly, or a "no" if not.
to only show companies who bought at least once in these 3 months. (my current result shows all of them including those that have already shut down)
Do let me know if there's a better way of going about it. All help appreciated. Thanks

Consider the following solution. I wrap your original query, slightly modified, and then use the monthly totals to create the two columns you want. If a company purchased in all three months, then it shows yes in the monthly purchase column. If a company made a purchase in at least one month, then it shows yes in the any purchase column.
I also removed your subqueries, and replaced them with CASE expressions using conditional aggregation. Also, I used the MONTH() function instead of checking the date ranges as you had originally, which is clumsy and even a bit error prone. It can be error prone because it is easy to botch an endpoint of a month, and also February doesn't always have 28 every year (it can have 29 in a leap year).
SELECT t.*,
CASE WHEN t.February > 0 AND t.March > 0 AND t.April > 0
THEN 'yes' ELSE 'no' END AS purchased_monthly,
CASE WHEN t.February > 0 OR t.March > 0 OR t.April > 0
THEN 'yes' ELSE 'no' END AS purchased_once
FROM
(
SELECT
fdebtorcode AS Debtorcode,
fcompanyname AS CompanyName, -- non aggregate column; this may be bad
SUM(CASE WHEN MONTH(fdate) = 2 THEN 1 ELSE 0 END) AS February,
SUM(CASE WHEN MONTH(fdate) = 3 THEN 1 ELSE 0 END) AS March,
SUM(CASE WHEN MONTH(fdate) = 4 THEN 1 ELSE 0 END) AS April
FROM tinvoicehistory
WHERE fdivision = 'Stationary'
GROUP BY fdebtorcode
) t
ORDER BY t.fdebtorcode

You may have a look at mysql's MONTH() and YEAR() functions.
Then you can try something like:
SELECT fdebtorcode As Debtorcode, fcompanyname As CompanyName,COUNT(*) AS purchases
FROM tinvoicehistory
WHERE fdivision = 'Stationary'
GROUP BY YEAR(fdate), MONTH(fdate) DESC, fdebtorcode
ORDER BY fdebtorcode
to start with.

Related

How to find difference in same column applying `group by` in SQL?

I need to find the difference between the points grouping by Id column.
Id | Year | Points
---+------+-------
1 | 2017 | 10
1 | 2018 | 20
2 | 2017 | 13
2 | 2018 | 16
3 | 2017 | 25
3 | 2018 | 20
Expected result:
Id | Points
---+-------
1 | 10
2 | 3
3 | -5
do aggregation
select
id,sum(case when year = 2018 then points end) -sum(case when year = 2017 then points end) as diff
from tablename group by id
If you want the difference between the years, you don't need group by:
select t2017.id, t2017.points as points_2017,
t2018.points as points_2018,
(t2018.points - t2017.points) as diff
from t t2017 join
t t2018
on t2017.id = t2018.id and
t2017.year = 2017 and
t2018.year = 2018;
You can do something very similar with conditional aggregation:
select id,
sum(case when year = 2017 then points end) as points_2017,
sum(case when year = 2018 then points end) as points_2018,
(sum(case when year = 2018 then points end) -
sum(case when year = 2017 then points end)
) as diff
from t
group by id;
SELECT *,
points - LAG(points) OVER ( PARTITION BY id
ORDER BY year ) delta_to_prev
FROM sourcetable
PS. Needs MySQL 8+.

Finding date where conditions within 30 days has elapsed

For my website, I have a loyalty program where a customer gets some goodies if they've spent $100 within the last 30 days. A query like below:
SELECT u.username, SUM(total-shipcost) as tot
FROM orders o
LEFT JOIN users u
ON u.userident = o.user
WHERE shipped = 1
AND user = :user
AND date >= DATE(NOW() - INTERVAL 30 DAY)
:user being their user ID. Column 2 of this result gives how much a customer has spent in the last 30 days, if it's over 100, then they get the bonus.
I want to display to the user which day they'll leave the loyalty program. Something like "x days until bonus expires", but how do I do this?
Take today's date, March 16th, and a user's order history:
id | tot | date
-----------------------
84 38 2016-03-05
76 21 2016-02-29
74 49 2016-02-20
61 42 2015-12-28
This user is part of the loyalty program now but leaves it on March 20th. What SQL could I do which returns how many days (4) a user has left on the loyalty program?
If the user then placed another order:
id | tot | date
-----------------------
87 12 2016-03-09
They're still in the loyalty program until the 20th, so the days remaining doesn't change in this instance, but if the total were 50 instead, then they instead leave the program on the 29th (so instead of 4 days it's 13 days remaining). For what it's worth, I care only about 30 days prior to the current date. No consideration for months with 28, 29, 31 days is needed.
Some create table code:
create table users (
userident int,
username varchar(100)
);
insert into users values
(1, 'Bob');
create table orders (
id int,
user int,
shipped int,
date date,
total decimal(6,2),
shipcost decimal(3,2)
);
insert into orders values
(84, 1, 1, '2016-03-05', 40.50, 2.50),
(76, 1, 1, '2016-02-29', 22.00, 1.00),
(74, 1, 1, '2016-02-20', 56.31, 7.31),
(61, 1, 1, '2015-12-28', 43.10, 1.10);
An example output of what I'm looking for is:
userident | username | days_left
--------------------------------
1 Bob 4
This is using March 16th as today for use with DATE(NOW()) to remain consistent with the previous bits of the question.
The following is basically how to do what you want. Note that references to "30 days" are rough estimates and what you may be looking for is "29 days" or "31 days" as works to get the exact date that you want.
Retrieve the list of dates and amounts that are still active, i.e., within the last 30 days (as you did in your example), as a table (I'll call it Active) like the one you showed.
Join that new table (Active) with the original table where a row from Active is joined to all of the rows of the original table using the date fields. Compute a total of the amounts from the original table. The new table would have a Date field from Active and a Totol field that is the sum of all the amounts in the joined records from the original table.
Select from the resulting table all records where the Amount is greater than 100.00 and create a new table with Date and the minimum Amount of those records.
Compute 30 days ahead from those dates to find the ending date of their loyalty program.
You would need to take the following steps (per user):
join the orders table with itself to calculate sums for different (bonus) starting dates, for any of the starting dates that are in the last 30 days
select from those records only those starting dates which yield a sum of 100 or more
select from those records only the one with the most recent starting date: this is the start of the bonus period for the selected user.
Here is a query to do that:
SELECT u.userident,
u.username,
MAX(base.date) AS bonus_start,
DATE(MAX(base.date) + INTERVAL 30 DAY) AS bonus_expiry,
30-DATEDIFF(NOW(), MAX(base.date)) AS bonus_days_left
FROM users u
LEFT JOIN (
SELECT o.user,
first.date AS date,
SUM(o.total-o.shipcost) as tot
FROM orders first
INNER JOIN orders o
ON o.user = first.user
AND o.shipped = 1
AND o.date >= first.date
WHERE first.shipped = 1
AND first.date >= DATE(NOW() - INTERVAL 30 DAY)
GROUP BY o.user,
first.date
HAVING SUM(o.total-o.shipcost) >= 100
) AS base
ON base.user = u.userident
GROUP BY u.username,
u.userident
Here is a fiddle.
With this input as orders:
+----+------+---------+------------+-------+----------+
| id | user | shipped | date | total | shipcost |
+----+------+---------+------------+-------+----------+
| 61 | 1 | 1 | 2015-12-28 | 42 | 0 |
| 74 | 1 | 1 | 2016-02-20 | 49 | 0 |
| 76 | 1 | 1 | 2016-02-29 | 21 | 0 |
| 84 | 1 | 1 | 2016-03-05 | 38 | 0 |
| 87 | 1 | 1 | 2016-03-09 | 50 | 0 |
+----+------+---------+------------+-------+----------+
The above query will return this output (when executed on 2016-03-20):
+-----------+----------+-------------+--------------+-----------------+
| userident | username | bonus_start | bonus_expiry | bonus_days_left |
+-----------+----------+-------------+--------------+-----------------+
| 1 | John | 2016-02-29 | 2016-03-30 | 10 |
+-----------+----------+-------------+--------------+-----------------+
Simple solution
Seeing how you do your first query, I guessed that when you are at the point where you look for the "expiration date", you already know that the user meets the 100 points over last 30 days. Then you can do this :
SELECT DATE_ADD(MIN(date),INTERVAL 30 DAY)
FROM orders o
WHERE shipped = 1
AND user = :user
AND date >= (DATE(NOW() - INTERVAL 30 DAY))
It takes the minimum order date of a user over the last 30 days, and add 30 days to the result.
But that really is a poor design to achieve what you want.
You would better to think further and implement what's next.
Advanced solution
In order to reproduce all the following solution, I have used the Fiddle that Trincot kindly built, and expanded it to test on more data : 4 users having 4 orders.
SQL FIddle http://sqlfiddle.com/#!9/668939/1
Step 1 : Design
The following query will return all the users meeting the loyalty program criteria, along with their earlier order date within 30 days and the loyalty program expiration date calculated from the earlier date, and the number of days before it expires.
SELECT O.user, u.username, SUM(total-shipcost) as tot, MIN(date) AS mindate,
DATE_ADD(MIN(date),INTERVAL 30 DAY) AS expirationdate,
DATEDIFF(DATE_ADD(MIN(date),INTERVAL 30 DAY), DATE(NOW())) AS daysleft
FROM orders o
LEFT JOIN users u
ON u.userident = o.user
WHERE shipped = 1
AND date >= DATE(NOW() - INTERVAL 30 DAY)
GROUP BY user
HAVING tot >= 100;
Now, create a VIEW with the above query
CREATE VIEW loyalty_program AS
SELECT O.user, u.username, SUM(total-shipcost) as tot, MIN(date) AS mindate,
DATE_ADD(MIN(date),INTERVAL 30 DAY) AS expirationdate,
DATEDIFF(DATE_ADD(MIN(date),INTERVAL 30 DAY), DATE(NOW())) AS daysleft
FROM orders o
LEFT JOIN users u
ON u.userident = o.user
WHERE shipped = 1
AND date >= DATE(NOW() - INTERVAL 30 DAY)
GROUP BY user
HAVING tot >= 100;
It is important to understand that this is only a one-shot action on your database.
Step 2 : Use your new VIEW
Once you have the view, you can get easily, for all users, the "state" of the loyalty program:
SELECT * FROM loyalty_program
user username tot mindate expirationdate daysleft
1 John 153 February, 28 2016 March, 29 2016 9
2 Joe 112 February, 24 2016 March, 25 2016 5
3 Jack 474 February, 23 2016 March, 24 2016 4
4 Averel 115 February, 22 2016 March, 23 2016 3
For a specific user, you can get the date you are looking for like this:
SELECT expirationdate FROM loyalty_program WHERE username='Joe'
You can also request all the users for which the expiration date is today
SELECT user FROM loyalty_program WHERE expirationdate=DATE(NOW))
But there are other easy possibilities that you'll discover after having played with your VIEW.
Conclusion
Make your life easier: learn to use VIEWS !
I am assuming your table looks like this:
user | id | total | date
-------------------------------
12 84 38 2016-03-05
12 76 21 2016-02-29
23 74 49 2016-02-20
23 61 42 2015-12-28
then try this:
SELECT x.user, x.date, x.id, x.cum_sum, d,date, DATEDIFF(NOW(), x.date) from (SELECT a.user, a.id, a.date, a.total,
(SELECT SUM(b.total) FROM order_table b WHERE b.date <= a.date and a.user=b.user ORDER BY b.user, b.id DESC) AS cum_sum FROM order_table a where a.date>=DATE(NOW() - INTERVAL 30 DAY) ORDER BY a.user, a.id DESC) as x
left join
(SELECT c.user, c.date as start_date, c.id from (SELECT a.user, a.id, a.date, a.total,
(SELECT SUM(b.total) FROM order_table b WHERE b.date <= a.date and a.user=b.user ORDER BY b.user, b.id DESC) AS cum_sum FROM order_table a where a.date>=DATE(NOW() - INTERVAL 30 DAY) ORDER BY a.user, a.id DESC) as c WHERE FLOOR(c.cum_sum/100)=MIN(FLOOR(c.cum_sum/100)) and MOD(c.cum_sum,100)=MAX(MOD(c.cum_sum,100)) group by concat(c.user, "_", c.id)) as d on concat(x.user, "_", x.id)=concat(d.user, "_", d.id) where x.date=d.date;
You will get a table something like this:
user | Date | cum_sum | start_date | Time_left
----------------------------------------------------
12 2016-03-05 423 2016-03-05 24
13 2016-02-29 525 2016-02-29 12
23 2016-02-20 944 2016-02-20 3
29 2015-12-28 154 2015-12-28 4
i have not tested this. But what i am trying to do is to create a table in descending order of id and user, and get a cumulative total column along with it. I have created another table by using this table with cumulative total, with relevant date (i.e. date from which date difference is to be calculated) for each user. I have left joined these two tables, and put in the condition x.date=d.date. I have put start_date and date in the table to check if the query is working.
Also, this is not the most optimum way of writing this code, but i have tried to stay as safe as possible by using sub queries, since i did not have the data to test this. Let me know if you face any error.

Finding MAX and MIN values for each same start and end week

There is a query I am trying to implement in which I am not having much success with in trying to find the MAX and MIN for each week.
I have 2 Tables:
SYMBOL_DATA (contains open,high,low,close, and volume)
WEEKLY_LOOKUP (contains a list of weeks(no weekends) with a WEEK_START and WEEK_END)
**SYMBOL_DATA Example:**
OPEN, HIGH, LOW, CLOSE, VOLUME
23.22 26.99 21.45 22.49 34324995
WEEKLY_LOOKUP (contains a list of weeks(no weekends) with a WEEK_START and WEEK_END)
**WEEKLY_LOOKUP Example:**
WEEK_START WEEK_END
2016-01-25 2016-01-29
2016-01-18 2016-01-22
2016-01-11 2016-01-15
2016-01-04 2016-01-08
I am trying to find for each WEEK_START and WEEK_END the high and low for that particular week.
For instance, if the WEEK is WEEK_START=2016-01-11 and WEEK_END=2016-01-15, I would have
5 entries for that particular symbol listed:
DATE HIGH LOW
2016-01-15 96.38 93.54
2016-01-14 98.87 92.45
2016-01-13 100.50 95.21
2016-01-12 99.96 97.55
2016-01-11 98.60 95.39
2016-01-08 100.50 97.03
2016-01-07 101.43 97.30
2016-01-06 103.77 100.90
2016-01-05 103.71 101.67
2016-01-04 102.24 99.76
For each week_ending (2016-01-15) the HIGH is 100.50 on 2016-01-13 and the LOW is 92.45 on 2016-01-14
I attempted to write a query that gives me a list of highs and lows, but when I tried adding a MAX(HIGH), I had only 1 row returned back.
I tried a few more things in which I couldn't get the query to work (some sort of infinite run type). For now, I just have this that gives me a list of highs and lows for every day instead of the roll-up for each week which I am not sure how to do.
select date, t1.high, t1.low
from SYMBOL_DATA t1, WEEKLY_LOOKUP t2
where symbol='ABCDE' and (t1.date>=t2.START_DATE and t1.date<=t2.END_DATE)
and t1.date<=CURDATE()
LIMIT 30;
How can I get for each week (Start and End) the High_Date, MAX(High), and Low_Date, MIN(LOW) found each week? I probably don't need a
full history for a symbol, so a LIMIT of like 30 or (30 week periods) would be sufficient so I can see trending.
If I wanted to know for example each week MAX(High) and MIN(LOW) start week ending 2016-01-15 the result would show
**Result:**
WEEK_ENDING 2016-01-15 100.50 2016-01-13 92.45 2016-01-14
WEEK_ENDING 2016-01-08 103.77 2016-01-06 97.03 2016-01-08
etc
etc
Thanks to all of you with the expertise and knowledge. I greatly appreciate your help very much.
Edit
Once the Week Ending list is returned containing the MAX(HIGH) and MIN(LOW) for each week, is it possible then on how to find the MAX(HIGH) and MIN(LOW) from that result set so it return then only 1 entry from the 30 week periods?
Thank you!
To Piotr
select part1.end_date,part1.min_l,part1.max_h, s1.date, part1.min_l,s2.date from
(
select t2.start_date, t2.end_date, max(t1.high) max_h, min(t1.low) min_l
from SYMBOL_DATA t1, WEEKLY_LOOKUP t2
where symbol='FB'
and t1.date<='2016-01-22'
and (t1.date>=t2.START_DATE and t1.date<=t2.END_DATE)
group by t2.start_date, t2.end_date order by t1.date DESC LIMIT 1;
) part1, symbol_data s1, symbol_data s2
where part1.max_h = s1.high and part1.min_l = s2.low;
You will notice that the MAX and MIN for each week is staying roughly the same and not changing as it should be different for week to week for both the High and Low.
SQL Fiddle
I have abbreviated some of your names in my example.
Getting the high and low for each week is pretty simple; you just have to use GROUP BY:
SELECT s1.symbol, w.week_end, MAX(s1.high) AS weekly_high, MIN(s1.LOW) as weekly_low
FROM weeks AS w
INNER JOIN symdata AS s1 ON s1.zdate BETWEEN w.week_start AND w.week_end
GROUP BY s1.symbol, w.week_end
Results:
| symbol | week_end | weekly_high | weekly_low |
|--------|---------------------------|-------------|------------|
| ABCD | January, 08 2016 00:00:00 | 103.77 | 97.03 |
| ABCD | January, 15 2016 00:00:00 | 100.5 | 92.45 |
Unfortunately, getting the dates of the high and low requires that you re-join to the symbol_data table, based on the symbol, week and values. And even that doesn't do the job; you have to account for the possibility that there might be two days where the same high (or low) was achieved, and decide which one to choose. I arbitrarily chose the first occurrence in the week of the high and low. So to get that second level of choice, you need another GROUP BY. The whole thing winds up looking like this:
SELECT wl.symbol, wl.week_end, wl.weekly_high, MIN(hd.zdate) as high_date, wl.weekly_low, MIN(ld.zdate) as low_date
FROM (
SELECT s1.symbol, w.week_start, w.week_end, MAX(s1.high) AS weekly_high, MIN(s1.low) as weekly_low
FROM weeks AS w
INNER JOIN symdata AS s1 ON s1.zdate BETWEEN w.week_start AND w.week_end
GROUP BY s1.symbol, w.week_end) AS wl
INNER JOIN symdata AS hd
ON hd.zdate BETWEEN wl.week_start AND wl.week_end
AND hd.symbol = wl.symbol
AND hd.high = wl.weekly_high
INNER JOIN symdata AS ld
ON ld.zdate BETWEEN wl.week_start AND wl.week_end
AND ld.symbol = wl.symbol
AND ld.low = wl.weekly_low
GROUP BY wl.symbol, wl.week_start, wl.week_end, wl.weekly_high, wl.weekly_low
Results:
| symbol | week_end | weekly_high | high_date | weekly_low | low_date |
|--------|---------------------------|-------------|---------------------------|------------|---------------------------|
| ABCD | January, 08 2016 00:00:00 | 103.77 | January, 06 2016 00:00:00 | 97.03 | January, 08 2016 00:00:00 |
| ABCD | January, 15 2016 00:00:00 | 100.5 | January, 13 2016 00:00:00 | 92.45 | January, 14 2016 00:00:00 |
To get the global highs and lows, just remove the weekly table from the original query:
SELECT wl.symbol, wl.high, MIN(hd.zdate) as high_date, wl.low, MIN(ld.zdate) as low_date
FROM (
SELECT s1.symbol, MAX(s1.high) AS high, MIN(s1.low) as low
FROM symdata AS s1
GROUP BY s1.symbol) AS wl
INNER JOIN symdata AS hd
ON hd.symbol = wl.symbol
AND hd.high = wl.high
INNER JOIN symdata AS ld
ON ld.symbol = wl.symbol
AND ld.low = wl.low
GROUP BY wl.symbol, wl.high, wl.low
Results:
| symbol | high | high_date | low | low_date |
|--------|--------|---------------------------|-------|---------------------------|
| ABCD | 103.77 | January, 06 2016 00:00:00 | 92.45 | January, 14 2016 00:00:00 |
The week table seems entirely redundant...
SELECT symbol
, WEEK(zdate)
, MIN(low) min
, MAX(high) max_high
FROM symdata
GROUP
BY symbol, WEEK(zdate);
This is a simplified example. In reality, you might use DATE_FORMAT or something like that instead.
http://sqlfiddle.com/#!9/c247f/3
Check if following query produces desired result:
select part1.end_date,part1.min_l,part1.max_h, s1.date, part1.min_l,s2.date from
(
select t2.start_date, t2.end_date, max(t1.high) max_h, min(t1.low) min_l
from SYMBOL_DATA t1, WEEKLY_LOOKUP t2
where symbol='ABCDE'
and (t1.date>=t2.START_DATE and t1.date<=t2.END_DATE)
group by t2.start_date, t2.end_date
) part1, symbol_data s1, symbol_data s2
where part1.max_h = s1.high and part1.min_l = s2.low
and (s1.date >= part1.start_date and part1.end_date)
and (s2.date >= part1.start_date and part1.end_date)

Select multiple years in separate columns

I have a table that stores the date and price of purchases.
For example
date | price
---------------------
2014-1-12 | 6.50
2014-2-34 | 10.99
2015-1-01 | 3.22
2015-2-14 | 4.12
And so on.
What I want to achieve: A query that outputs the SUM of the purchases grouped per month of the year.
The IMPORTANT thing is, though, that I need to have the different years in COLUMNS to be able to make a graph with a separate line for each year. So the output I need is this:
MONTH | 2014 | 2015
JAN | 123.23 | 99.1
FEB | 457.00 | 122.00
MAR | 299.99 | 789.12
... |
NOV | 333.33 | 10.99
DEC | 100.00 | 20.10
Is this even possible? I searched quite a long time for things like "year on year" query etc. But I could not find anything.
Any help is greatly appreciated!
Just use conditional aggregation:
select monthname(date) as mon,
sum(case when year(date) = 2014 then price else 0 end) as price_2014,
sum(case when year(date) = 2015 then price else 0 end) as price_2015
from table t
group by monthname(date)
order by max(month(date));

RDBMS Splitting a column into many columns with respect to a field

I am creating a web application based on attendance system. I am using MySQL and PHP. I have a table for every employee that has took attendance and they keep adding when the employees takes attendance on the machine. I extracted the employees that has has attendance 2 times per day which are in and out, and I added them to a table with columns. Now I want to extract the attendances for the employees that has took attendance 1 per day or more than 2 per day which they are error and i want to add them to a table employee time1, time2 and time3.
I have a table as this design, it's an attendance system,
I need to split the time if the attendance count per day was not equal to 2, which are error logs.
id pd time date
1 5 07:05 08/07/2014
2 4 18:02 07/07/2014
3 1 07:05 06/07/2014
4 1 07:06 06/07/2014
5 1 18:00 06/07/2014
I need to add them to a table in the database and to be split in that form with respect the pd and date.
id pd time1 time2 time3 .... date
1 5 07:05 08/07/2014
2 4 18:02 07/07/2014
3 1 07:05 07:06 18:00 06/07/2014
sorry guys its my first post in my whole entire life , so forgive me if am doing something wrong or not complete
The following result, which isn't an exact match to your request, but it may get you started. It doesn't attempt to list all the starting times, but does count the number of entries per pd per date so any that are not equal to 2 can be detected easily.
| ID | PD | MIN_TIME | COUNT_TIME | MAX_TIME | DATE |
|----|----|----------|------------|----------|-------------------------------|
| 3 | 1 | 07:05 | 3 | 18:00 | June, 07 2014 00:00:00+0000 |
| 2 | 4 | 18:02 | 1 | 18:02 | July, 07 2014 00:00:00+0000 |
| 1 | 5 | 07:05 | 1 | 07:05 | August, 07 2014 00:00:00+0000 |
The query for this is:
SELECT
MIN(id) AS id
, pd
, MIN(time) AS min_time
, COUNT(time) AS count_time
, MAX(time) AS max_time
, date
FROM AttendanceTbl
GROUP BY
pd
, date
ORDER BY
pd
, date
You can review it at this SQLFIDDLE
Suggestions.
include table name(s) as well as the sample data.
consider using http://sqlfiddle.com to provide a working example for development of the solution
Note:
- I really don't like using date or time as field names, I know MySQL lets you do it but I can't recommend it.
Here is the solution thank alot for all :)
$sql="SELECT MIN(id) AS id,date, enrollnumber,
MIN((CASE WHEN ordinal=1 THEN time END)) AS time1,
MIN((CASE WHEN ordinal=2 THEN time END)) AS time2,
MIN((CASE WHEN ordinal=3 THEN time END)) AS time3,
MIN((CASE WHEN ordinal=4 THEN time END)) AS time4,
MIN((CASE WHEN ordinal=5 THEN time END)) AS time5,
MIN((CASE WHEN ordinal=6 THEN time END)) AS time6,
MIN((CASE WHEN ordinal=7 THEN time END)) AS time7,
MIN((CASE WHEN ordinal=8 THEN time END)) AS time8,
MIN((CASE WHEN ordinal=9 THEN time END)) AS time9,
MIN((CASE WHEN ordinal=10 THEN time END)) AS time10
FROM (
SELECT t.id, t.enrollnumber, t.time, t.date,
(SELECT 1+COUNT(*)
FROM attlog AS sub
WHERE sub.enrollnumber=t.enrollnumber AND
sub.date=t.date AND (
sub.time<t.time OR
sub.time=t.time AND sub.id<t.id)) AS ordinal
FROM attlog AS t
) AS x
GROUP BY enrollnumber, date";