Get each day of the week as columns MySQL and PHP - php

I am writing a web application in PHP with MySQL.
I have a table called counts and this is how data is stored in that table:
Table: counts
id counts location_id media_id created_at
--------------------------------------------------
1 50 1 1 2017-03-15
2 30 2 1 2017-03-15
3 80 1 2 2017-03-15
4 20 1 1 2017-03-16
5 100 2 2 2017-03-16
For every unique location_id, media_id and created_at, I store count.
I have another table locations which is like this:
Table: locations
id name
----------------
1 Location 1
2 Location 2
3 Location 3
4 Location 4
5 Location 5
This is the SQL Query I have at the moment:
select sum(counts.count) as views, locations.name as locations, DAYNAME(counts.created_at) AS weekday from `counts` inner join `locations` on `locations`.`id` = `counts`.`location_id` where `counts`.`created_at` between '2016-12-04' and '2016-12-10' group by `weekday`, `counts`.`location_id`;
This is how the data is displayed:
locations weekday views
-----------------------------------
Location 1 Mon 50
Location 1 Tue 30
Location 2 Mon 20
Location 2 Tue 70
I'm creating reports and I would like to run a query such that all the days of the week appear as a column with their corresponding values as the view count for that day of the week. I want something like this:
locations mon tue wed thu fri sat sun
-------------------------------------------------
Location 1 40 60 51 20 40 20 30
Location 2 80 60 100 24 30 10 5
Is the above possible in MySQL or I would have to use PHP to achieve that? If so, how do I go about it?
Any help will be appreciated, thanks.
NB: The sample data is not accurate.

It's possible to achieve this result with MySQL, using conditional aggregation.
The trick is to use a conditional test in an expression in the SELECT list to determine whether to return a value of count.
Something like this:
SELECT l.name AS `locations`
, SUM(IF(DATE_FORMAT(c.created_at,'%a')='Mon',c.count,0)) AS `mon`
, SUM(IF(DATE_FORMAT(c.created_at,'%a')='Tue',c.count,0)) AS `tue`
, SUM(IF(DATE_FORMAT(c.created_at,'%a')='Wed',c.count,0)) AS `wed`
, SUM(IF(DATE_FORMAT(c.created_at,'%a')='Thu',c.count,0)) AS `thu`
, SUM(IF(DATE_FORMAT(c.created_at,'%a')='Fri',c.count,0)) AS `fri`
, SUM(IF(DATE_FORMAT(c.created_at,'%a')='Sat',c.count,0)) AS `sat`
, SUM(IF(DATE_FORMAT(c.created_at,'%a')='Sun',c.count,0)) AS `sun`
FROM `locations` l
LEFT
JOIN `counts` c
ON c.location_id = l.id
AND c.created_at >= '2016-12-04'
AND c.created_at < '2016-12-04' + INTERVAL 7 DAY
GROUP BY l.name
ORDER BY l.name
NOTE:
With the sample data, there are two rows for location_id=1 and created_at='2016-03-15', so this query would return a total of 130 for tue (=50+80), not 50 (as shown in the sample output of the existing query).

Related

Mysql GROUP BY and Union based on date condition

Here is my query - it mostly works, but I can see it failing on one condition - explained after the query:
$firstDay = '2020-03-01' ;
$lastDay = '2020-03-31' ;
SELECT * FROM clubEventsCal
WHERE ceFreq!=1
AND (ceDate>='$firstDay' AND ceDate<='$lastDay')
UNION SELECT * FROM clubEventsCal
WHERE ceFreq=1
AND (ceDate>='$firstDay' AND ceDate<='$lastDay')
GROUP BY ceStopDate ORDER BY ceID,ceDate ;
The first select gives me all Event records between the two dates. The second select gives me grouped/summarized Event records between the two dates. The problem though is if the value ceDate spans days across two months: IE: 2020-03-30 thru 2020-04-02. When I pull the records for March, all is good - the above query pulls the 2020-03-30 record (grouped) as the first instance of the 4 days/records - allowing us to charge for a single 4 day event. But when I pull the records for April its also going to pull 2020-04-01 as a new grouped Event record for the last two days of the 4 day event and try to charge the customer for a new Event - when in fact those two days were already a part of March's bill.
How can I write the query so that when ceDate starts in Month X but ends in Month Y that when records are pulled for Month Y its not trying to pull records that actually belong to an Event that started in Month X?
Examples of an Event record would look like this:
rid | ceID | ceActive | ceFreq | ceDate | ceStopDate
------------------------------------------------
1 1108 1 3 2020-03-09 | 2020-03-09
2 1111 1 2 2020-03-15 | 2020-03-15
3 1112 1 2 2020-03-17 | 2020-03-17
4 1117 1 1 2020-03-30 | 2020-04-02
5 1117 1 1 2020-03-31 | 2020-04-02
6 1106 1 3 2020-03-21 | 2020-03-21
7 1110 1 2 2020-03-05 | 2020-03-05
8 1113 1 2 2020-03-24 | 2020-03-24
9 1117 1 1 2020-04-01 | 2020-04-02
10 1117 1 1 2020-04-02 | 2020-04-02
The above query pulls all records where ceFreq != 1, and it pulls a single record for the ceFreq = 1 records (rids: 4 & 5). For March, we don't necessarily care that ceID 1117 spills into April. But when we pull records for April - we need to exclude rid 9 & 10, because the Event (ceID=1117), was already accounted for in March.
SELECT * FROM clubEventsCal
...
GROUP BY ceStopDate
This is gibberish.
MySQL (depending on configuration) allows it without choking - but it's semantically wrong and stands out as an anti-pattern.
There are some edge cases where the values returned might contain significant data, but they very unusual. Trying to explain a problem with code which does not work is perhaps not a good strategy.
Looking at your code, its possible that you don't need a union - but there's not enough information in your example records to say if this would actually give the result you expect (it will be significantly faster depending on your indexes):
SELECT IF(cefreq=1, rid, null) AS consolidator
, ceid
, cefreq
, MIN(cedate), MAX(cedate)
, ceStopDate
FROM clubEventsCal
WHERE cID=1001
AND ceActive!=2
AND (ceDate>='$firstDay' AND ceDate<='$lastDay')
GROUP BY IF(cefreq=1, rid, null)
, ceid
, cefreq
, ceStopDate
;
I would have added the ORDER BY - but I don't know where clId came from. Also This will give different resuts to what I think you were trying to achieve for any record where cefreq is null (if you really do want to exclude them, add a predicate in the WHERE clause).

Convert MySQL time stamp into DAY then sort by MAX

Trying to write a mySQL query that selects the most recent(**) number of times a light was turned on by a user.
**Most recent being all the times the light was turned on for the most recent day in the DB.
Sample Table:
DB Name: LLL
Table Name: Lights
UserID | LightOn | LightOff
-----------------------------------------------------
3 | 2018-01-08 09:00:00 | 2018-01-08 09:03:00
3 | 2018-01-08 10:15:00 | 2018-01-08 10:17:00
3 | 2018-01-07 15:00:00 | 2018-01-07 15:05:00
So, From this table, we can tell that
UserID 3 (Bob) turns the light on:
2 times on January 8th (at 9AM for 3 minutes and 10:15AM for 2 minutes) &
1 times on January 7th (at 3PM for 5 mins)
I want my query to return 2, because there are 2 records for the most recent day of January 8th.
I'm at the point where I can only get the number of records:
SELECT COUNT(C.LightOff) AS count FROM LLL.Lights AS C
WHERE C.UserID = 3
ORDER BY C.LightsOff DESC
I get the following back:
count
-------
3
I need to figure out a way to convert the time stamp into a DAY and get all the records that match that MAX Day.
The desired result is:
count
-------
2
Any ideas?
Assuming you have a proper datetime value in you lightoff column
you could get the most recent day and join with your count
select count(*) from LLL.Lights
inner join (
select max(date(LightOff)) max_date
FROM LLL.Lights
WHERE UserID = 3
) t on t.max_date = date(LightOff)

Select data from two tables with no join condition

I'd appreciate if someone could help. I have two tables that have no relationship:
Table_1
ID credittype creditamount date time
------------------------------------
1 abc 10 2016-01-18 11:29:59 am
2 def 20 2016-01-20 4:35:58 pm
3 def 20 2016-01-21 4:35:58 pm
Table_2
ID debitetype debiteamount date time
------------------------------------
1 abc 10 2016-01-18 11:29:59 am
2 def 20 2016-01-19 4:35:58 pm
3 def 20 2016-01-21 4:35:58 pm
i just want to display these tables values
like that
credittype creditamount debitetype debiteamount date time
---------------------------------------------------------
abc 10 2016-01-18 11:29:59 am
def 20 2016-01-19 4:35:58 pm
def 20 2016-01-20 4:35:58 pm
i will try this query
select * from Table_1 union select * from Table_2;
i did not get answer any one help me?
Using union only gets distinct values from both tables. Try using "union all" to return all values. Refer to http://www.w3schools.com/sql/sql_union.asp.
Note , joining 2 table that don't have any condition not good , and it discovered that you don't design your database structure very well.
so , try like this :
select tb1.credittype,tb1.creditamount,tb1.debitetype,tb1.debiteamount,tb1.date,tb1.time from table1,table2
it gives you all record in table one and table two

How do I order by one column and then push latest chunk of same column id's to top?

For example I have table:
| id | title | created_at |
1 12:00
2 13:00
2 14:00
1 15:00
I want same id numbers to be near each other. In this case 1 1 2 2 or 2 2 1 1 AND order same chunks of id's by created_at time so the chunk of id's which own the latest created_at stays on top, then goes one, having highest created_at compared to 3rd chunk of id's and so on. How do I do it?
orderBy('id', 'desc')->orderBy('created_at', 'desc')->get(); // orders id's to same id chunks, but it doesn't sort that the chunk with latest id chunk (1 1) created_at at the top.
orderBy('created_at', 'desc')->orderBy('id', 'desc')->get(); // gives the latest created_at at top and so on, but same id's arent close to each other.
Bigger example:
| id | title | created_at |
1 12:00
2 13:00
1 15:00
2 15:00
1 17:00
3 18:00
1 19:00
3 20:00
Want to anchieve that foreach($table_rows as $row) { } would give me result:
3 20:00
3 18:00
1 19:00
1 17:00
1 15:00
1 12:00
2 15:00
2 13:00
I know it's hard to do with mysql alone. How do I do this in php easiest way?
I bet I have to sort by id first and then push each id's chunk relative to each other by latest created_at.
You need to get the information of the most recent date for each id. Here is a method using join and an aggregation:
select t.*
from table t join
(select t.id, max(created_at) as maxca
from table t
group by t.id
) tt
on t.id = tt.id
order by tt.maxca desc, id;
The rest is just the order by using the maximum value.
I don't know how to express this in laravel, but your question is also tagged mysql.

SQL query to count number of days with reappearing entries

I have a database with access controll log entries:
time : datetime (this is the access timestamp)
src: text (this is the userid)
I want to get a list out of it that shows how many users from the current day had already access on how many days during the past 7 days. The result should look like this:
number of days with access | count
1 | 30
2 | 54
3 | 123
4 | 843
5 | 3490
6 | 71
7 | 23
What I have so far:
The query below returns the number of users with log entry on 2015-03-08 that had also an entry on 2015-03-07.
SELECT Count(DISTINCT a.src)
FROM contacts AS a
LEFT JOIN contacts AS b
ON a.src = b.src
WHERE a.time BETWEEN Cast('2015-03-08 05:00:00' AS DATETIME) AND Cast('2015-03-09 05:00:00' AS DATETIME)
AND b.time BETWEEN Cast('2015-03-07 05:00:00' AS DATETIME) AND Cast('2015-03-08 05:00:00' AS DATETIME)
But I'm stuck with getting the count for each dayby number of days as described above. If there is no 'sql only' solution it would be ok as well to have an (performant) approach using php. Thanks for any help..
I don't see any reason why do you need to join b table.
SELECT
DAY(a.time),
COUNT(DISTINCT a.src)
FROM contacts AS a
WHERE a.time
BETWEEN (TIMESTAMP(CURDATE()) - INTERVAL 1 WEEK)
AND TIMESTAMP(CONCAT(CURDATE(),' 23:59:59'))
GROUP BY DAY(a.time)

Categories