I try to get value for every date even there is no transaction:
$transaction = Transaction::whereBetween(DB::raw('DATE(created_at)'), ['2015-12-01', '2016-12-31'])
->where('vendor_id', $row)
->groupBy('vendor_id', DB::raw('DATE(created_at)'))
->selectRaw('FLOOR(SUM(amount)) as total, DATE(created_at) as date')
->orderBy('created_at')->get();
return $transaction;
The result I get:
[{"total":"81739","date":"2015-12-22"},{"total":"82912","date":"2015-12-26"},{"total":"55912","date":"2015-12-27"},{"total":"55599","date":"2015-12-30"},{"total":"77626","date":"2015-12-31"}];
How can I get value for every date if it's no value insert it with 0?.
Thanks.
Update: added new solution (without joining)
The idea is very simple: if you've no transactions today, today's date is not in your transaction table.
The solution is simple too: you've to store every date like so:
Run every day at 00:00:00 a cronjob that inserts an empty transaction. So you get every date in your transaction table without joining to another table.
Be care: only if there's a 'real' transaction for a date, the 'fake' (empty) transaction should be deleted.
Alternative 1:
Create a table with dates (with id and date), for example from 2015-01-01 to 2025-12-31
Select your dates and left join to the transaction table
Example query:
SELECT * FROM dates d LEFT JOIN transactions t ON t.date_id = d.id
Instead of storing the date in your transaction table, your store the foreign key to the dates table.
Alternative 2:
Create a table for dates (with id and date)
Run every 00:00:00 a cronjob that insert the current date
Select your dates and left join to the transaction table
Alternative 3:
Generate with php's DateInterval a date range
Link the records from your transaction table to every right date of the range
Related
I want to do a query with thousands of tuples. I need to save the first ID, last ID and date saved in a historic table by day in a new table. I have data from 2020 to 2022. Every day could 600.000 rows or more. I have thought two solutions:
Doing a query every time with limit 600.000 and save the first id, last id and date, all of this order by dates or ids.
Doing a query day by day and get the first and the last id.
The problems are that these querys could delay so much because i am doing orderings.
I´m doing this with SQL and need execute this in PHP with a cron every day to save the data of the day. First, i´m building the new table with the data of past.
Someone would know one tip or antoher form to do this.
THANKS!
You can do this (result here)
select date, min(id) as min, max(id) as max
from logs
group by date
I'm working on some php code having to do with creating recurring invoices. I need some help thinking through the logic. I think ultimately I will end up with a type of Insert/Select statement. The php code that is ultimately generated here will run in a nightly cron.
First, let me show you some of my table items for a frame of reference and explain a bit about what they do. The table is called Invoice and there are other tables associated with it, but I think if I can nail now this one table's logic, I can apply it to the others.
ID = This is the primary key that auto increments and also happens to be the invoice number.
recurring = This is whether the invoice is one time or recurring. O=One time, M=Monthly etc
recurred = This will be set to N on insert and is the logic I'm using to see if the next recurring invoice that will be created in the table should be based off this invoice or not. When a new recurring invoice is inserted into the table, this will be changed to Y so its not processed again by the cron job.
date = This is the date that the invoice was inserted/created. For example, if date the invoice was created is the 15th of this month, the next invoice created will be the 15th of next month. Essentially M represents monthly. However, I haven't figured out yet what to do about a created date of the 31st but the next month only has 30 days, could use some help here.
So here is my logic. First, I run a query to find all rows that are not O and recurred = N.
$result = mysql_query("SELECT * FROM invoice WHERE recurring != 'O' and recurred
= 'N'") or die(mysql_error());
Next, I work on each kind of recurring M for monthly, Q for quarterly, etc..
if recurring = M
if todays date day = 'date' day
copy line item into a new row with the following changes: new ID, todays date, and mark the recurred to N for the new insert (will mark the old one Y)
Obviously that is not actual code, just what I'm thinking about.
So my questions are 1.) How to create a insert select statement to deal with changed variables and 2.) how to deal with dates where if the invoice was created on the 31st of one month and the next month only has 30 days (or 28).
take only rows that need to be re-inserted:
SELECT *, DATEDIFF(`date`, NOW()) as diff FROM invoice WHERE recuring != 'O'
AND recured = 'N' AND diff >= 30;
iterate through that list and use a new date for each row by calculating it with:
$newDate = date("Y-m-d", strtotime($oldDate+" +1 month"));
This should solve your 30th day of month problem.
Now insert newly created data.
Also, good advice to use enum for recuring field, and boolean for recured.
I have made somewhat similar application. In my case I had 2 tables: actual invoices and recurring invoices. Recurring table had one field called NextDate - the date when next invoice should be created. One time invoice is added directly into invoices table, otherwise the cron job scans the recurring table and checks for a NextDate value. If it is in past then new invoice is generated into invoices table and NextDate is updated (normally incremented by month).
In my case incrementing by month changes month value only by 1. For example in MySQL
DATE_ADD('2008-01-31', INTERVAL 1 MONTH) --> 2008-02-29
I have a table in DB which contains summaries for days. Some days may not have the values.
I need to display table with results where each column is a day from user selected range.
I've tried to play with timestamp (end_date - start_date / 86400 - how many days in report, then use DATEDIFF(row_date, 'user_entered_start_date') and create array from this indexes), but now I've got whole bunch of workarounds for summer time :( Any examples or ideas how to make this correct?
P.S. I need to do this on PHP side, because DB is highly loaded.
Try the DateTime object:
$reportdate=date_create($user_startdate);
$interval=new DateInterval('P1D');//1 day interval
$query="SELECT s.start_date, s.end_date, s.info
FROM summary s
WHERE s.start_date>='$user_startdate'
AND s.end_date<='$user_enddate'";
$r=mysqli_query($db,$query);
while($row=$r->fetch_assoc()){
$rowdate=create_date($row['start_date']);
while($reportdate < $rowdate) {//won't work if $rowdate is a timestamp!
//output $reportdate and blank row
$reportdate->add($interval); //increment date
}
//output $rowdate and $row[info]
$reportdate->add($interval); //increment date
}
ETA another option:
Based on your comments, it may be easier to dynamically generate the missing dates. For this you'll need an integer table, the number of dates that should appear in your report output, a start date and a date increment.
In your db create a table called numbers and insert the numbers 0 through 9:
CREATE TABLE numbers (
num int(10) unsigned NOT NULL,
PRIMARY KEY (num)
);
The numbers table can be used for making sequences of integers. For instance, to get a sequence from 1 to 20:
SELECT i FROM (
SELECT 10*n1.num + n2.num AS i
FROM numbers n1 CROSS JOIN numbers n2) nums
WHERE i BETWEEN 1 AND 20
ORDER BY i ASC;
If you left join a sequence query like the above to your regular query, you should be able to generate both real and blank rows. e.g.
SELECT alldates.d, mystuff.* FROM
(SELECT date_add($start_date, interval i day) AS d FROM
(SELECT 10*n1.num + n2.num AS i
FROM numbers n1 CROSS JOIN numbers n2
ORDER BY i ASC) nums
WHERE i <= DATEDIFF($end_date,$start_date)) alldates
LEFT JOIN mystuff
ON alldates.d = mystuff.somedate
ORDER BY $whatever;
You could "pre-load" the database with blank values. Then do UPDATE queries instead of inserts. All your days with no data will be pre-populated. You can create a simple script that creates a month's/year's/decade's worth of (blank) data and run it as often as you need. Then you never have to worry about how the days with no data get into the database - you start with your data "zeroed" out.
Using MySQL and PHP; I'm trying to build an index that contains the averages from table_1 grouped by:
type, name, hour, day, month, year
So I need to know which combination's of values are in table_1 so I know what to put in my AVG() queries.
What I want is to figure out all the different combination's that can be made when comparing the following rows in the table:
type
name
hour
day
month
year
Here's an example of table_1:
ID|type|name|location|amount|year|month|day_num|day|hour|minute|second
1|car|ben|1|1.00|2010|10|01|Friday|03|05|45
1|car|bob|1|3.00|2010|10|01|Friday|04|05|45
2|cow|bob|2|2.00|2009|07|12|Sunday|09|10|12
2|cow|ben|2|4.00|2009|07|12|Sunday|10|10|12
So what I would end up with is:
type|name|year|month|day|hour
car|ben|2010|10|01|Friday|03
car|bob|2010|10|01|Friday|04
cow|bob|2009|07|12|Sunday|09
cow|ben|2009|07|12|Sunday|10
How would I format a query to do that?
Since you just want the combinations that exist, you can simply run this query:
SELECT DISTINCT type, name, hour, day, month, year FROM table
This goes through all of the rows, and for each combination that exists in the table, that combination will be output once in the result set.
Currently I have two tables and the following sql statement which correctly retrieves the items in the events table ordered by their dates in the event_dates table:
SELECT * FROM events, event_dates
WHERE events.id=event_dates.event_id
AND events.preview=0 AND event_dates.start_date>=now()
ORDER BY event_dates.start_date ASC,event_dates.start_time ASC LIMIT 3
Now I want to add an extra AND to make sure only the events on the next weekend are set. The date column is in a standard mysql date format (YYYY-MM-DD). Got stuck on this bit. Cheers.
Use PHP strtotime() to get the start and end timestamp of the weekend:
$we_start=strtotime('next saturday');
$we_end=strtotime('next monday')-1;
Then do a sql query to search for timestamps BETWEEN them.
select * from mytable where UNIX_TIMESTAMP(mydatefield) BETWEEN $we_start AND $we_end
Hope that helps.