MySQL incorrect result on group by - php

I am counting unique ip visits for user account. I am getting different result on checking total visits vs grouped user account visits
My table structure is like this
id userid userip status date
1 xxxx 11111 1 unix timestamp
2 yyyy 11122 1 unix timestamp
3 zzzz 11133 1 unix timestamp
4 cccc 11144 1 unix timestamp
I am doing query like this
$date1 = strtotime("yesterday midnight");
$date2 = strtotime("today midnight");
SELECT `userid`, COUNT(DISTINCT `userip`) AS `total` FROM `stats`
WHERE (`date` >= $date1 AND `date` < $date2) AND `status`=1
This gives result as 5644
But when I group by userid result is different
$date1 = strtotime("yesterday midnight");
$date2 = strtotime("today midnight");
SELECT `userid`, COUNT(DISTINCT `userip`) AS `total` FROM `stats`
WHERE (`date` >= $date1 AND `date` < $date2) AND `status`=1 GROUP BY `userid`
while($row=mysqli_fetch_assoc($result)){
$total=$total+$row['total'];
}
This gives result as 6312
Please see why there is different result on group by
Thanks
EDIT
Result is correct if I don't count DISTINCT

its obvious that the count wont match as in your first query you have used distinct with count now the rows fetched is 5644.
Which may have count with the values like 2,3,4,5 so if you need the same count as group by you need to add all the count column which will be same as your group by query
if you need unique users the use below query
SELECT DISTINCT `userip` AS `userip` FROM `stats`
WHERE (`date` >= $date1 AND `date` < $date2) AND `status`=1
For user with unique ip
SELECT userid,userip from stats GROUP BY userip HAVING COUNT(*) >=1

Related

Select count for each day in a month

I have a sms tracker database with a date column in the format 02/25/2018 04:12:52 pm. I want to count the no of sms sent each day to display it in bar chart.
I could only count sms sent by a user using this query "SELECT count(*) as user_count from table where username = 'CTC01'". How can i get an array of count for each day in a particular month
Since OP's date_column is VARCHAR type. We use STR_TO_DATE function:
SELECT DATE(STR_TO_DATE(date_column, "%m/%d/%Y %r")), COUNT(*)
FROM table
GROUP BY DATE(STR_TO_DATE(date_column, "%m/%d/%Y %r"));
Use DATE function, to convert a datetime expression to a date. Then use GROUP BY to get COUNT datewise.
In case, you want to get data for a specific user (eg: CTC01) and datewise. You can do the following:
SELECT DATE(STR_TO_DATE(date_column, "%m/%d/%Y %r")), COUNT(*)
FROM table
WHERE username = 'CTC01'
GROUP BY DATE(STR_TO_DATE(date_column, "%m/%d/%Y %r"));
I see your date format is 'm/d/Y H:i:s'. So, to get the total for each day in a month, you have to do a comparison against the least time in that month and the highest time in that month. So, the query for February 2018 would be:
SELECT DATE(date_column), COUNT(*)
FROM jobs
where created_on >= '02/01/2018 00:00:00' and created_on < '03/01/2018 00:00:00'
GROUP BY DATE(date_column);
To get for a particular user, simply append a where clause to the query above like so:
SELECT DATE(date_column), COUNT(*)
FROM jobs
where created_on >= '02/01/2018 00:00:00' and created_on < '03/01/2018 00:00:00' and username = 'CTC01'
GROUP BY DATE(date_column);
EDIT
Since your date column is varchar, you first have to convert it to datetime. So run this query instead:
SELECT DATE(DATE_FORMAT(STR_TO_DATE(date_column, '%c/%e/%Y %H:%i'), '%Y-%m-%d %H:%m:%s')) as Date, COUNT(*)
FROM jobs
where created_on >= '02/01/2018 00:00:00' and created_on < '03/01/2018 00:00:00'
GROUP BY DATE(DATE_FORMAT(STR_TO_DATE(date_column, '%c/%e/%Y %H:%i'), '%Y-%m-%d %H:%m:%s'));

Reduce number of mysql count query for different time interval

I am using below query:
$select_jan1 = "SELECT count(*) FROM users WHERE timeStamp BETWEEN '2015-01-01' and '2015-01-31'";
$select_feb1 = "SELECT count(*) FROM users WHERE timeStamp BETWEEN '2015-02-01' and '2015-02-28'";
$select_mar1 = "SELECT count(*) FROM users WHERE timeStamp BETWEEN '2015-03-01' and '2015-03-31'";
Is there way to put this 3 query in one ?
Thank You
You can put the values in three columns, using conditional aggregation:
SELECT SUM(timeStamp BETWEEN '2015-01-01' and '2015-01-31') as cnt_201501,
SUM(timeStamp BETWEEN '2015-02-01' and '2015-02-28') as cnt_201502,
SUM(timeStamp BETWEEN '2015-03-01' and '2015-03-31') as cnt_201503
FROM users;
Do note that this logic ignores that values on the last day of each month. Better logic is:
SELECT SUM(timeStamp >= '2015-01-01' and timestamp < '2015-02-01') as cnt_201501,
SUM(timeStamp >= '2015-02-01' and timestamp < '2015-03-01') as cnt_201502,
SUM(timeStamp >= '2015-03-01' and timestamp < '2015-04-01') as cnt_201503
FROM users
WHERE timeStamp >= '2015-01-01' and timeStamp < '2015-04-01';
With whole months, I do so:
SELECT
count(*)
FROM
(SELECT
year(timestamp) AS year_, month(timestamp) AS month_
FROM
users) s
WHERE
s.year_ = 2015 AND s.month_ >=4

Select all records where the day of month is between 30 and 15

I have a table called Liabilities which has the following columns loan_name, institution_name, starting_date, emi, closing_date. The type of starting_date and closing_date is varchar.
I am trying to write a query which gets data (EMI reminder) where the day of starting_date is between 30 and 15.
Example:- I have written the following query, it works in scenarios like if today's date is 07-11-2014. Below query will return data for next 15 days but if the date is 30-11-2014 then it won't return results for next 15 days. Kindly check below query.
SELECT
user_id,
owner_name as client_name,
loan_name as scheme_name,
institution_name as institution_name,
starting_date as date_of_deduction,
emi as amount,
closing_date,
"Liabilities" as instrument
FROM (`liability`)
WHERE `user_id` = '46'
AND
(STR_TO_DATE(starting_date, "%d") between STR_TO_DATE("30-11-2014", "%d")
and STR_TO_DATE("15-12-2014", "%d"))
AND
(STR_TO_DATE(closing_date, "%d-%m-%Y") >= STR_TO_DATE("30-11-2014", "%d-%m-%Y"))
I want records between 30th - 15th of every month as I want to send emails to clients. I have 3 records for reference
1) starting date :- 17-09-2012
2) starting date :- 06-10-2010
3) starting date :- 21-08-2014
I want the query to return record 2 which is 06-10-2010 since the day of month is between 30th and 15th.
Since starting_date field is a varchar and you store the date in dd-mm-yyyy format, you need to convert starting_date to datetime using STR_TO_DATE() function like below
STR_TO_DATE(starting_date, "%d-%m-%Y")
then check if the day of the above value is 30 or above, or 15 or below using DAYOFMONTH() function
DAYOFMONTH(STR_TO_DATE(starting_date, "%d-%m-%Y")) >= 30
OR DAYOFMONTH(STR_TO_DATE(starting_date, "%d-%m-%Y")) <= 15
The following query should return the correct records
SELECT
user_id,
owner_name as client_name,
loan_name as scheme_name,
institution_name as institution_name,
starting_date as date_of_deduction,
emi as amount,
closing_date,
"Liabilities" as instrument
FROM (`liability`)
WHERE `user_id` = '46'
AND
(DAYOFMONTH(STR_TO_DATE(starting_date, "%d-%m-%Y")) >= 30
OR DAYOFMONTH(STR_TO_DATE(starting_date, "%d-%m-%Y")) <= 15)
Try this:
SELECT
user_id,
owner_name as client_name,
loan_name as scheme_name,
institution_name as institution_name,
starting_date as date_of_deduction,
emi as amount,
closing_date,
"Liabilities" as instrument
FROM
(liability)
WHERE
user_id = '46'
AND TO_DAYS(STR_TO_DATE(starting_date, "%d-%m-%Y")) > TO_DAYS(STR_TO_DATE("30-11-2014", "%d-%m-%Y"))
AND TO_DAYS(STR_TO_DATE(starting_date, "%d-%m-%Y")) < TO_DAYS(STR_TO_DATE("15-12-2014", "%d-%m-%Y"))
AND TO_DAYS(STR_TO_DATE(closing_date, "%d-%m-%Y")) >= TO_DAYS(STR_TO_DATE("30-11-2014", "%d-%m-%Y"))

How do I create an array of data for 30 days from database table where some dates are not entered?

Let's say you have a simple table with:
ID IP CREATED_DATE
-------------------------------------------------------
1 111.111.111.111 2011-03-28 14:12:31
2 111.111.111.111 2011-03-29 03:38:12
3 222.222.222.222 2011-04-02 12:04:45
4 111.111.111.111 2011-04-02 22:13:23
5 333.333.333.333 2011-04-03 05:53:15
6 222.222.222.222 2011-04-05 02:13:51
7 111.111.111.111 2011-04-07 11:45:34
I need to query the last 30 days and get a count of unique ips and total rows per day even if there are no entries for that day. So days that are not entered would obviously just be 0 as the array value for both unique and total rows.
If you have an integers table, you can generate all the days in your desired timespan and join those with the tallies of IPs for each day appearing in your table:
SELECT timespan."day",
COALESCE(num_uniq_ips, 0) AS num_uniq_ips,
COALESCE(num_records, 0) AS num_records
FROM (SELECT DATE_SUB(CURRENT_DATE, INTERVAL i DAY) AS "day"
FROM integers
WHERE i < 30) timespan
LEFT JOIN (SELECT DATE(created_date) AS "day",
COUNT(DISTINCT ip) AS num_uniq_ips,
COUNT(1) AS num_records
FROM so6025149
GROUP BY 1) tallies
ON timespan."day" = tallies."day"
ORDER BY timespan."day" ASC;
You can create a temporary table, populate it with the past 30 days and use that in your query. From PHP code:
// create table with past 30 days
$buffer = "CREATE TEMPORARY TABLE IF NOT EXIST past_month (val DATETIME NOT NULL );";
$now = new DateTime(now);
for($i=30;$i>0;$i++)
{
$buffer += "INSERT INTO past_month VALUES(" . $now . ");";
$now->sub(new DateInterval('P1D'));
}
mysql_query($buffer);
// the query
$sql= "SELECT
val,
(SELECT COUNT(IP)
FROM table
WHERE DATE_FORMAT(CREATED_DATE,'%Y-%m-%d) = DATE_FORMAT(val,'%Y-%m-%d') As UNIQUE_IPS
FROM
past_month";
$result = mysql_query($sql);
SELECT COUNT(DISTINCT column_name)
FROM table_name
WHERE ( DateTime > (GetDate()-30) )
something along those lines

unix timestamps and php

I have a list of unix timestamps in a database, and I wanting to select the ones that are from today.
i.e If today is Tueday, I want to get all the timestamps that were made today? Is it possible? Is there such a things as strtotime("Today")?
Any help would be great
you can use mktime() to generate the timestamp for the start of the day and then find the database entries with a timestamp greater than that.
$start = strtotime(date('Y-m-d 00:00:00')); // Current date, at midnight
$end = strtotime(date('Y-m-d 23:59:59')); // Current date, at 11:59:59 PM
then, you can just select where the timestamp is between the above 2 timestamps:
"SELECT FROM `foo` WHERE `timestamp` BETWEEN '{$start}' and '{$end}'"
You can convert the unix timestamps to sql dates in the SQL using FROM_UNIXTIME(), then compare those to NOW()
SELECT * FROM `tablename` WHERE DATE(FROM_UNIXTIME(`dateFld`)) = DATE(NOW());
Check if DAY(NOW()) and MONTH(NOW()) and YEAR(NOW()) is equal to appropriate value of DAY(timestamp) and MONTH(timestamp) and YEAR(timestamp).
select timestamp from table where DAY(NOW()) = DAY(timestamp) AND MONTH(NOW()) = MONTH(timestamp) AND YEAR(NOW()) = YEAR(timestamp)
If you're using mysql:
SELECT * FROM table WHERE DATE(NOW()) = DATE(FROM_UNIXTIME(timestampcol))
FROM_UNIXTIME(somefield) can be compared to CURDATE() assuming you're using MySQL
SELECT * FROM mytable WHERE FROM_UNIXTIME(datefield,'%Y-%m-%d') = CURDATE();
ETA:
Okay, I was assailed by doubt when this answer was marked down. So I went and did a couple of tests. Given MySQL it definitely works. So why the downmod?
Consider this test which outputs 2 identical fields for every row in a table:
SELECT FROM_UNIXTIME(UNIX_TIMESTAMP(CURDATE()),'%Y-%m-%d') a , CURDATE() b
FROM tablewithsomerows
WHERE FROM_UNIXTIME(UNIX_TIMESTAMP(CURDATE()),'%Y-%m-%d') = CURDATE();

Categories