PHP SQL SELECT sum(value) WHERE x LIKE '%$month%' (foreach) - php

I have a table:
user_id | month | value
1 | firstname_1_2017 | 5
1 | secondname_1_2017 | 4
1 | firstname_2_2017 | 7
1 | secondname_2_2017 | 8
1 | firstname_3_2017 | 5
1 | secondname_3_2017 | 4
1 | firstname_4_2017 | 7
1 | secondname_4_2017 | 8
I need sum "value" for each month with a foreach.
firstname_1_2017 + secondname_1_2017 = 9
firstname_2_2017 + secondname_2_2017 = 15
I have an array for the months:
$array_months = array( '1','2','3','4','5','6','7','8','9','10','11','12' );
So I wrote (in php):
SELECT sum(value)
FROM table
WHERE user_id = $user_id
AND month LIKE '%_$array_months_2017%'
When I write a foreach, the result is the same sum of all values repeat for each month (Example: Jan 48 points, Feb 48 points, Mar 48 points...).
I'd like this result with a foreach:
You have "9" points in January
You have "15" points in February
etc...

You should think about changing your DB design to separate Month and Year from user's name.
Not a good approach, but a solution will be to extract month year from month string and group it to calculate sum for each month.
SELECT SUBSTR(month, LOCATE('_', month) + 1) AS month_year, SUM(value)
FROM users
WHERE user_id = 1
GROUP BY month_year;
Working Fiddle

You can extract the numbers from your month column values with the SUBSTRING_INDEX() function like this, giving 3_2017.
SUBSTRING_INDEX('firstname_3_2017', '_', -2)
You can then transform that to a date-ish string with CONCAT(), yielding 1_3_2017.
CONCAT('1_',SUBSTRING_INDEX('firstname_3_2017', '_', -2))
Then you can turn that into an actual DATE value with STR_TO_DATE().
STR_TO_DATE(CONCAT('1_',SUBSTRING_INDEX('firstname_3_2017', '_', -2)),'%d_%m_%Y')
Cool, now you have a datatype you can actually work with. You can now use an aggregate query to get good stuff. Leveraging Samir's fiddle: http://sqlfiddle.com/#!9/8aabd/6/0
SELECT
SUM(value) value,
user_id,
STR_TO_DATE(CONCAT('1_',SUBSTRING_INDEX(month, '_', -2)),'%d_%m_%Y') month_begin
FROM table
GROUP BY user_id, STR_TO_DATE(CONCAT('1_',SUBSTRING_INDEX(month, '_', -2)),'%d_%m_%Y')
Summary: simple transformations of your month column can give you usable information.
Pro tip: when designing your tables try not to stuff values with different meanings into single columns. Read about normalization.
Pro tip 2: use actual DATE, TIMESTAMP, or DATETIME values for storing time-based information whenever possible. Date arithmetic and date-based indexing are powerful parts of DBMS servers.

Related

Total sum of rows that has same column value

I want to get the total value of multiable columns hat has same value.
Ex. I want to sum all Angry reacts in month 1, 2, 3 .... etc. but I want the total of each month alone
MONTH | ANGRY | DISGUST | HAPPY |......|PEOPLE
0 | 99 | 87 | 92 | | 250
1 | 200 | 45 | 12 | | 400
.
.
.
11 |....................................|
Database Table
You seem to be looking for simple aggregation:
select
month,
sum(angry) angry,
sum(disgust) disgust,
sum(happy) happy,
...
sum(people) people
from mytable
group by month
Also, as commented by Strawberry, you should probably consider fixing your schema. Instead of having a bunch of columns, you could have just three:
date -- a legitimate datetime column
mood -- angry, disgust, ...
value -- an integer value
With this set-up in place, your would be abe to generate a report with a query like:
select year(date), month(date), mood, sum(value)
from mytable
group by year(date), month(date), mood
use groupBy
$data = DB::table('your_table_name')->select(
DB::raw('sum(angry) as sums'))
->groupBy('months')
->get();

Calculate Average between MySQL results

I need to calculate the avg days between date of sales:
My DB is like this:
id | customer | creation_date | payment_date
1 | 234 | 2017/07/6 | 2017/07/8
34 | 234 | 2017/08/4 | 2017/08/10
53 | 234 | 2017/09/15 | 2017/09/17
67 | 234 | 2017/10/1 | 2017/07/6
So I need to calculate de difference of days (creation_date) between Order 1 and Order 34, Order 34 and Order 53, Order 53 and Order 67, etc...
and calculate an AVG of days depending the number of results.
So I know how to calculate the difference of days between 2 dates using this small script:
$seconds=strtotime($date1) - strtotime($date2);
$difference=intval($seconds/60/60/24);
$positive = $difference * -1;
but I donĀ“t know how to take the date of the las result and compare it with the next result.
Please someone who can help me with this enigma. Thanks!
I could be misunderstanding what you are looking for, but I would think something like this should work
(TO_DAYS(MAX(creation_date))-TO_DAYS(MIN(creation_date))) / (COUNT(1)-1)
This will get you the total days between the first and last; and divide by the number of "spaces" between orders.
Edit: ....and if you wanted to treat orders on the same date as a single order, you can just change COUNT(1) to COUNT(DISTINCT creation_date).
...all this assumes the db designer was sane and actually used DATE data types for date values.
To summarize, the average of the span sizes should be the same as the total span divided by the number of spans.
You can keep track of the previous result using a variable outside of the loop to get your MySQL table and then run the loop through the rows of the table:
$last_positive = 0;
while ($row = $result->fetch_assoc()){
$date1 = $row['creation_date'];
$date2 = $row['payment_date'];
$seconds=strtotime($date1) - strtotime($date2);
$difference=intval($seconds/60/60/24);
$positive = abs($difference);
//DO SOME COMPARISON HERE
echo($last_positive >= $positive);
$last_positive = $positive;
}
I'd also suggest using abs to get the absolute value instead of multiplying by -1.
SOLVED WITH THIS:
SELECT DATEDIFF(MAX(creation_date), MIN(creation_date)) / (COUNT(creation_date) - 1) AS SaleAverage FROM table WHERE customer = '$customer'

Splitting month into weeks and using GROUP BY

This is a question, which I could not find answer to anywhere. Okay. here it is.
I have two date ranges (This month and the last month)
Last month - 01/01/2015 (January 1 2015) to 31/01/2015
This month - 01/02/2015 (1st Feb 2015) to 28/02/2015
Now, each month has weeks. I have a table with column created_at. I want to fetch all the records week-wise into an array (to plot a graph) with their corresponding sum(value) or count(value) .
So it will be something like this:
Last Month:
Week 1 - 25
Week 2 - 34
etc.
This Month:
Week 1: 55
Week 2: 56
etc.
The date is in this format in created_at: 2015-07-21 01:27:14 (Y-m-d H:i:s)
In MySql You can use WEEK() to get the number of the week (from 1 to 53)
O you can use WEEKDAY() or DAYOFWEEK() the first bigins on Monday the second on Sunday.
You can use them into a GROUP BY with HAVING
Something like:
SELECT count(*)
FROM `YourTable`
WHERE `created_at` >= '2015-10-01' AND `created_at`< '2015-11-01'
GROUP BY WEEK(`created_at`)
To use the workaround you found You need to do something similar:
create a table named "numbers" with a field "id" (autoincrement) and 31 rows (one for each day of a month)
Then use a query like this:
SELECT count(i.created_at)
FROM
(SELECT DATE_FORMAT(DATE_ADD('2015-12-01', INTERVAL -n.id DAY), '%Y-%m-%d') AS AllDays
FROM numbers n) AS DaysOfMonth
Left Join
YourTableName i
ON STR_TO_DATE(i.created_at, '%Y-%m-%d') = DaysOfMonth.AllDays
GROUP BY WEEK(AllDays)
(try to adapt it to your needs)
What you need to do is group by the week and then sum the values. Here's a simple example of how it might work:
SELECT DATE_FORMAT(created_at,'%Y-%V') as interval, SUM(units_sold) as total_sold
FROM sales
GROUP BY DATE_FORMAT(created_at,'%Y-%V')
What you'll be getting is the year ant week number (ex. 2015-50) and the sum from that interval.
A table like this:
+----+------------+---------------------+
| id | units_sold | created_at |
+----+------------+---------------------+
| 1 | 2 | 2015-01-01 09:00:00 |
| 2 | 4 | 2015-01-04 10:00:00 |
| 3 | 1 | 2015-01-12 12:00:00 |
| 4 | 4 | 2015-01-16 13:00:00 |
+----+------------+---------------------+
Would result to:
+----------+------------+
| interval | total_sold |
+----------+------------+
| 2015-01 | 6 |
| 2015-03 | 5 |
+----------+------------+
I think it is useful for you...
SELECT GROUP_CONCAT(id), COUNT(id) AS idcount,SUM(id) AS idsum,
MONTHNAME(order_created_date) AS month_name, WEEK(order_created_date)
AS weeks FROM orders GROUP BY WEEK(order_created_date)

SQL count distinct visits of a customer with opening hours from 10am to 6am

I am writing a customer loyalty software for a club that opens from 10am to 6am everyday. The data is store in MYSQL and I'd like to count the customer's total visits for the month.
I am using count(distinct(date)) but if the player came at 5pm and stayed till 3am with 2 transactions at 10pm and 2am. It will be counted as 2 visits instead of 1.
I have a transaction table with the columns listed below:
ps: anything in the brackets () is not real data. I get about 2000 transactions a day. I am also able to change the table structure
Transaction_ID | Date(not Date/Time) | Customer_ID | Item | price | timestamp
1 | 11-06-2015 (6pm) | Jane | drink| 2.00 | 156165166
2 | 09-06-2015 (2pm) | Jane | drink| 2.00 | 1433858493
3 | 10-06-2015 (3am) | Jane | drink| 2.00 | 1433906073
4 | 06-06-2015 (6pm) | Jane | drink| 2.00 | 156165166
Current code returns {4, Jane}. The answer I'm looking for is {3,Jane}. Transaction {2,3} should be considered as one visit
SELECT count(distinct(Date)) as visit, Customer_ID
FROM transaction
GROUP BY Customer_ID
WHERE timestamp BETWEEN $timestamp1 AND $timestamp2
$timestamp1 = strtotime("first day of february +10am");
$timestamp2 = strtotime("first day of march +6am");
How do you suggest to accurately count the total visits below? I am able to change the table structure from Date to Date/time.
The easiest answer with least changes to my codes.
SELECT count(DISTINCT(DATE(DATE_SUB(from_unixtime(timestamp),INTERVAL 6 HOUR))) as visit, Customer_ID
FROM transaction
GROUP BY Customer_ID
WHERE timestamp BETWEEN $timestamp1 AND $timestamp2
The easiest way is to shift your datetime (date,timestamp?) field back for 6 hours in a SQL statement and then you will get an interval in one day from 4AM to 12PM:
DISTINCT(DATE(DATE_SUB(dt,INTERVAL 6 HOUR)))
SQLFiddle demo
Here is the code you need:
SELECT
Customer_ID 'Customer ID'
, COUNT(DISTINCT visit) as 'Visits per month'
, MONTH(visit) 'Month'
, YEAR(visit) 'Year'
FROM
(SELECT
*
, CASE
WHEN (t_timestamp > Date_StartDate AND t_timestamp < Date_EndDate)
THEN d_date
WHEN (t_timestamp < Date_StartDate)
THEN date_add(d_date, INTERVAL -1 DAY)
END 'visit'
FROM
(SELECT *
, DATE_ADD(CAST(d_date AS DATETIME), INTERVAL 10 HOUR) Date_StartDate
, DATE_ADD(DATE_ADD(cast(d_date AS DATETIME), INTERVAL 6 HOUR), INTERVAL 1 DAY) Date_EndDate
FROM transactions) Results
) Results
GROUP BY customer_id, month(visit), year(visit)
Also, here is a SQLFiddle with the results of the code.
I haven't used the exact format for your Customer_ID (I've used INTEGER instead of VARCHAR) and didn't use the exact dates you used in your example, but obviously it should work for anything.
Consider adjusting the name of the columns used in my query to the appropriate column names and you should be fine.

MySQL query to retrieve maximum number of items within a given time period

I've a table like attached.
I want to find the "number of items in a certain month which has maximum entries in the database".
For instance, Jan has 10 entries, Feb has 13 entries, Mar has 8 entries.
I want to find the the number 13 for Feb from the database as it has the max entries. How do I check the time range in the query?
You can group all of your realeasedates by month and year to get a count like this:
SELECT MONTH(releasedate) AS month, YEAR(releasedate) as year, count(r_id) AS number
FROM my_table
GROUP BY YEAR(releasedate), MONTH(releasedate)
ORDER BY YEAR(releasedate), MONTH(releasedate)
This'll give you something like this:
+--------+--------+--------+
| month | year | number |
+--------+--------+--------+
| 1 | 2013 | 13 |
| 2 | 2013 | 8 |
Then you could select the maximum like this:
SELECT MONTH(releasedate) AS month, YEAR(releasedate) as year, count(r_id) AS number
FROM my_table
GROUP BY YEAR(releasedate), MONTH(releasedate)
ORDER BY count(r_id)
LIMIT 1
Which'll give you:
+--------+--------+--------+
| month | year | number |
+--------+--------+--------+
| 4 | 2013 | 19 |
+--------+--------+--------+
Which'll represent the highest month
SELECT COUNT(*) FROM `mytable` WHERE releasedate >= DATE '2013-02-01' AND releasedate <= DATE '2013-02-28'
That should work
EDIT:
As suggested by lafor...
WHERE YEAR(releasedate)=2013 AND MONTH(releasedate)=2
should also work

Categories