Group By alternative in Laravel 5.4 - php

I have a table Schedule with 3 columns (id, ref_number, pay_date). Each ref_number has a pay_date in every month. So the table looks something like this:
id | ref_number | pay_date
-----------------------------
1 | A001 | 2018-06-29
1 | A001 | 2018-07-29
1 | A002 | 2018-06-30
1 | A002 | 2018-07-30
1 | A002 | 2018-08-30
1 | A003 | 2018-06-29
I want to fetch only the earliest record for every ref_number that have pay_date between today and a date (30 or 31 days from today). The below query works fine in Mysql (I would pass the dates dynamically later).
SELECT id,ref_number,MIN(pay_date) FROM schedule
WHERE (pay_date BETWEEN '2018-06-30' AND '2018-07-30')
GROUP BY ref_number
I know we can turn the mysql "strict" to false in Database config file and the Group By would behave as expected, but without having to change that is there any other way around this problem?
What would be the eloquent equivalent for this query? With or without groupby.

After searching for a while, I came across an answer using nested select statement and gave it a try in Laravel. It worked exactly as I wanted. Here's the sweet piece of code:
Schedule::select(DB::raw(id, ref_number, MIN(pay_date) as pay_date))
->from(DB::raw("(SELECT *
FROM schedule
WHERE (pay_date > CURDATE()))
temp")
)
->groupBy('temp.ref_number')
->get();

Related

MYSQL query for getting daily data for last 30 days count even if data not exists for any date

I have table : "orders"(order_id,order_processed_date). I want count of orders per day for last 30 days. If any date has 0 orders, then it should print 0.
Something like this:
total | date
1 | 2018-10-20
0 | 2018-10-19
0 | 2018-10-18
0 | 2018-10-17
2 | 2018-10-16
0 | 2018-10-15
1 | 2018-10-14
0 | 2018-10-13
0 | 2018-10-12
1 | 2018-10-11
1 | 2018-10-10
5 | 2018-10-09
1 | 2018-10-08
and so on upto 2018-09-20. I already searched in stackoverflow and get some queries but did not find exact solution for this. I get result using below query but it has only records which date has not 0 orders:
SELECT COUNT(order_id) AS total, DATE(order_processed_date) AS date
FROM orders
WHERE order_processed_date BETWEEN '2018-09-20' AND '2018-10-20'
GROUP BY DATE(order_processed_date)
ORDER BY order_processed_date DESC
Can please someone help me to give me result as I required.
You could try to use a calendar table, an example of which is described here. Basically, this is a table with an ordered list of dates (+ extra properties if you'd like).
You could use the calendar table to LEFT JOIN your orders on, and use a GROUP BY to get the results e.g. per month.
Best of luck.

MySql: select data based on timestamp and dynamic interval

In my MySql DB I have these fields:
id | email_id | interval | start_at | last_sent_at
--- | -------- | ---------- | ------------------- | -------------------
1 | 8293 | +6 months | 2017-06-14 16:59:54 | 2017-06-14 16:59:54
--- | -------- | ---------- | ------------------- | -------------------
2 | 8904 | (NULL) | 2017-05-14 12:32:45 | (NULL)
I am trying to create a dynamic way for users to set a schedule for an email job in laravel. The idea is to use laravel's commands scheduler to run a command that checks all scheduled emails and then runs them if they haven't been sent (whereNull(last_sent_at)) or if the last time they were sent was more than the current time minus 6 months or 12 months or what ever they select for that field.
I have tried several different queries to get this to work to no avail. In laravelized code I have tried the following...
$schedules = Schedule::selectRaw('schedules.schedulable_id, schedules.schedulable_type, UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(schedules.interval) as NowPlusInterval')
->whereNull('last_sent_at')
->orWhere('schedules.last_sent_at', '>=', 'NowPlusInterval')
->get();
and
$schedules = Schedule::whereNull('last_sent_at')
->orWhereRaw('schedules.last_sent_at >= NOW()-'.Carbon::parse('schedules.interval')->toDateTimeString())
->get();
and many many other variations. The second one is the simplest to me but because I am passing the db field to a php function it doesn't recognize that I am trying to get the value from the db field (not parse a string called 'schedules.interval'). Is there anyway write this field into a variable that Carbon can parse or is there a raw mysql query that I can run that will give me all the fields where last_sent_at is null and where now minus the interval listed is more than the last_sent_at.
I've racked my brain on this for hours. Any help is appreciated.
Try using mysql's DATE_SUB() and INTERVAL functions. Something to the effect of:
->whereRaw('last_sent_at >= SUB_DATE(NOW(), INTERVAL 6 MONTH)')
or perhaps this:
->whereNull('last_sent_at')
->orWhere(DB::raw('DATE(`schedules.last_sent_at`) >= DATE_SUB(NOW(), INTERVAL `schedules.interval`')))
->get();

Join Row to Previous Closest Date Row

Database data:
id | account | date | random_data
1 | 1 | 01/01/2013 | qw
2 | 2 | 05/01/2013 | er
3 | 2 | 09/01/2013 | ty
4 | 1 | 05/01/2013 | ui
5 | 2 | 11/01/2013 | op
6 | 1 | 12/01/2013 | as
Hi, so let's say I want the records starting from 05/01/2013 - note that prev_date for the 1st row still shows an earlier date than 05/01 meaning that the whole table still needs to be searched.
Result data:
account | cur_date | random_data | prev_date | prev_rand_data
1 | 05/01/2013 | ui | 01/01/2013 | qw
1 | 12/01/2013 | as | 05/01/2013 | ui
2 | 05/01/2013 | er | null | null
2 | 09/01/2013 | ty | 05/01/2013 | er
2 | 11/01/2013 | op | 09/01/2013 | ty
So I'm not sure what is the best, most optimized query I could use for this. I'm not opposed to a php solution but not sure how much better that would be. Some ideas I've considered:
Some sort of join on the same table - not sure how though
Sub queries on the select -
select date as cur_date
, (select max(date)
from table
where date < cur_date
group by account)
as prev_date... - this seems like it could be incredibly intensive
Session variables - set a session variable on each row which will be the previous data for the next row e.g.
select date as cur_date
, #prev_date as prev_date
, #prev_date:=date...
Has anyone had any experience with a problem like this and was there a good solution? Are there any positives negatives with any of the ideas I have that could cause problems in the future?
I would use a combination of sql and application code. Since I am not a php programmer, I will only describe the logic to use for the application part.
First the query.
select account, date, random_data
from thetable
where date >= YourDateVariable
union
select account, date, random_data
from thetable join
(select account acc, max(date) maxdate
from thetable
where date <= YourDateVariable
group by account) x on account = acc and date = max(date)
where date <= YourDateVariable
order by account, date
For the application code, do this:
Set a variable called ThisAccount to 0.
Set a row counter variable to 0.
Create an empty 2D array
Start looping through your query results
Put the account value and random data into the first two columns
of the next available row of the array
Compare the account value to the value of the ThisAccount variable.
If they are the same, get the previous date and random data from
the previous row in the array.
Set the ThisAccount variable to the current account value.
Increment your row counter variable
End of loop.

PHP MYSQL Detect if new row is added

Any Idea how can I identify if there is new client added on my database.
I was thinking about identifying it thru date_added field.
id client_name date_added
---------------------------------
1 ABC 2013-01-02
2 XYZ 2013-01-03
3 EFG 2013-01-02
4 HIJ 2013-01-05
as you can see a new client added HIJ on 2013-01-05.
I was looking with this kind of result:
Client List
Total NO: 4
New Client
Total No: 1
Client Name: HIJ
add a field new to the table, default it to 1, on page load use that for the select and set it to 0 to indicate its not longer new.
It's hard to tell but based on your comment ...my reference date is 1 month interval... you might be looking for something like this
SELECT id, client_name, new_count, total_count
FROM
(
SELECT id, client_name
FROM clients
WHERE date_added BETWEEN CURDATE() - INTERVAL 1 MONTH AND CURDATE()
) c CROSS JOIN
(
SELECT
(
SELECT COUNT(*) new_count
FROM clients
WHERE date_added BETWEEN CURDATE() - INTERVAL 1 MONTH AND CURDATE()
) new_count,
(
SELECT COUNT(*) total_count
FROM clients
) total_count
) t
Obviously you can easily change CURDATE() with any other reference date in the past in this query and you get results for that date.
Lets assume that you have following sample data
+------+-------------+------------+
| id | client_name | date_added |
+------+-------------+------------+
| 1 | ABC | 2013-05-13 |
| 2 | XYZ | 2013-06-13 |
| 3 | EFG | 2013-06-13 |
| 4 | HIJ | 2013-08-11 |
+------+-------------+------------+
and today is 2013-08-13 then the output from the query will be
+------+-------------+-----------+-------------+
| id | client_name | new_count | total_count |
+------+-------------+-----------+-------------+
| 4 | HIJ | 1 | 4 |
+------+-------------+-----------+-------------+
You could remember, in your webpage or PHP script, the highest ID value previously seen. Or the highest timestamp (better than a date) previously seen.
I prefer ID or Version numbers for concurrency-related stuff (locking, finding the latest etc) -- since they should be defined to be ascending, can't suffer "same millisecond" collisions, and are more efficient.
I assume you're going to hold the "state" of your application (as to what the user has seen) in hidden fields in the form, or somesuch. This would then track the "last seen" and allow you to identify "newly added" since the last pageview.
If you expect to identify newly added when coming from a different page or logging onto the application, you'll need to store the "state" in the database instead.
That depends on what you consider NEW. You have to define what you're going to compare the records against (reference date). Once you define it, you could use a query like the following:
SELECT * FROM client WHERE date_added >= '$date'
where $date is the reference date.

Get average time between several dates for one entry

I am not really sure how to explain this, but basically what I am trying to do is get the average time that it takes between a set of dates. This is what the database table looks like
id | offer_id | user | date | date_completed
----------------------------------------------------
1 | 123 | test | 1352265988 | 1352265995
2 | 123 | admin| 1352266004 | 1352266022
3 | 123 | kira | 1352264754 | 1352271946
I need to get the average time between the 2 timestamps and calculate them together so I can echo it out in my code. I am sure this would be done with a foreach statement, but I have no idea how to go about doing it. If there is anyone that can point me in the right direction that would be great!
Unless I'm missing something, you can just do it in a single query:
SELECT AVG(date_completed - date)
FROM myTable
Use the SQL-function AVG for each of the colums (date and date_completed). Than, you can just substract each average time and you will get your result you are searching for.

Categories