Is there any "Laravel-ish" way to capture the earliest and latest dates from multiple date fields in several laravel tables?
table for model1:
fielda| fieldb| date1
table2 for model2:
fieldc| fieldd| date1| date2
I couldn't find any elegant way to do it. My current approach is something like:
$model1_dates = Model1::select(\DB::raw('MIN(date1) as min_date'), \DB::raw('MAX(date1) as max_date'))->get();
$model2_dates = Model2::select(\DB::raw('MIN(date2) as min_date'), \DB::raw('MAX(date2) as max_date'))->get();
$model2_dates2 = Model2::select(\DB::raw('MIN(date2) as min_date'), \DB::raw('MAX(date1) as max_date'))->get();
afterwards, I will compare the results and get the minimum and maximum of those...
What approaches would be better?
I've used Laravel, but I'm not a fan - So I don't really know "Laravel-ish" or what it is. If you mean "something that looks simple", then it could be
$minDate = collect([
Model1::min('date1'),
Model2::min('date1'),
Model2::min('date2'),
])->min();
$maxDate = collect([
Model1::max('date1'),
Model2::max('date1'),
Model2::max('date2'),
])->max();
That's neat - But that are 6 queries to the DB.
A single raw SQL would be
select min(min_date) as min_date, max(max_date) as max_date
from (
select min(date1) as min_date, max(date1) as max_date from table1
union all
select min(date1) as min_date, max(date1) as max_date from table2
union all
select min(date2) as min_date, max(date2) as max_date from table2
) x
or
select min(min_date) as min_date, max(max_date) as max_date
from (
select min(date1) as min_date, max(date1) as max_date
from table1
union all
select min(least(date1, date2)) as min_date,
max(greatest(date1, date2)) as max_date
from table2
) x
depending on what indexes you have. The first query performs better, when every date column has its own index.
Yes there are functions like max()and min(). Also you can unite two queries with the union() method
Aggregate Methods
Union
And you can also use orderBy with laravel. Works also on dates.
OrderBy
Fastest way:
$minDateModel1 = Model1::min('date1');
$maxDateModel1 = Model1::max('date1');
Or:
$minDateModel1 = Model1::oldest('date1')->first()->pluck('date1');
$maxDateModel1 = Model1::latest('date1')->first()->pluck('date1');
Or:
$minDateModel1 = Model1::max('date1');
//should be the oldest first, newest at last
$minDateModel1 = Model1::orderBy('date1', 'asc')->first()->pluck('date1');
$maxDateModel1 = Model1::orderBy('date1', 'asc')->first()->pluck('date1');
Should do the trick:
$minDateModel1 = Model1::max('date1');
$minDateModel21 = Model2::max('date1');
$minDateModel22 = Model2::max('date2');
$allMinDates = $allMinDates->merge($minDateModel1);
$allMinDates = $allMinDates->merge($minDateModel21);
$allMinDates = $allMinDates->merge($minDateModel22);
$min = $allMinDates->min();
Related
Is it possible to order when the data comes from many select and union it together? Such as
In this statement, the vouchers data is not showing in the same sequence as I saved on the database, I also tried it with "ORDER BY v_payments.payment_id ASC" but won't be worked
( SELECT order_id as id, order_date as date, ... , time FROM orders WHERE client_code = '$searchId' AND order_status = 1 AND order_date BETWEEN '$start_date' AND '$end_date' ORDER BY time)
UNION
( SELECT vouchers.voucher_id as id, vouchers.payment_date as date, v_payments.account_name as name, ac_balance as oldBalance, v_payments.debit as debitAmount, v_payments.description as descriptions,
vouchers.v_no as v_no, vouchers.v_type as v_type, v_payments.credit as creditAmount, time, zero as tax, zero as freightAmount FROM vouchers INNER JOIN v_payments
ON vouchers.voucher_id = v_payments.voucher_id WHERE v_payments.client_code = '$searchId' AND voucher_status = 1 AND vouchers.payment_date BETWEEN '$start_date' AND '$end_date' ORDER BY v_payments.payment_id ASC , time )
UNION
( SELECT return_id as id, return_date as date, ... , time FROM w_return WHERE client_code = '$searchId' AND w_return_status = 1 AND return_date BETWEEN '$start_date' AND '$end_date' ORDER BY time)
Wrap the sub-select queries in the union within a SELECT
SELECT id, name
FROM
(
SELECT id, name FROM fruits
UNION
SELECT id, name FROM vegetables
)
foods
ORDER BY name
If you want the order to only apply to one of the sub-selects, use parentheses as you are doing.
Note that depending on your DB, the syntax may differ here. And if that's the case, you may get better help by specifying what DB server (MySQL, SQL Server, etc.) you are using and any error messages that result.
You need to put the ORDER BY at the end of the statement i.e. you are ordering the final resultset after union-ing the 3 intermediate resultsets
To use an ORDER BY or LIMIT clause to sort or limit the entire UNION result, parenthesize the individual SELECT statements and place the ORDER BY or LIMIT after the last one. See link below:
ORDER BY and LIMIT in Unions
(SELECT a FROM t1 WHERE a=10 AND B=1)
UNION
(SELECT a FROM t2 WHERE a=11 AND B=2)
ORDER BY a LIMIT 10;
I am trying to create a summary report for to capture daily stats. Basically I need the outcome similar to:
Table_Name Updated_Rows Created_Rows Date
Table 1 10 5 2019-04-23
Table 2 17 55 2019-04-23
Now I can fetch the individual values using basic commands:
select count(*) as created_rows
from accounts
where date(updated_at) = date(now())
and
select count(*) as created_rows
from accounts
where date(created_at) = date(now())
Can also combine the data using the UNINON ALL,
(SELECT table_name FROM information_schema.tables where table_name='accounts')
UNION ALL
(select count(*) as created_rows from accounts where date(created_at) = date(now()))
UNION ALL
(select count(*) as updated_rows from accounts where date(updated_at) = date(now()))
However the output I get is kind of stacked vertically and I wish to retain the labels/column names and would want to add data row by row for all the tables I want to assess.
I am sure there is an easier way but I can't seem the find a way out to get this done. Don't need the final query, just help me with a direction to look towards.
For a single table, you can do the following:
SELECT 'account' AS 'Table_Name'
, SUM(date(updated_at) = date(now())) 'Updated_Rows'
, SUM(date(created_at) = date(now())) 'Created_Rows'
, date(now()) AS 'Date'
FROM accounts
where SUM(date(updated_at) = date(now())) is basically the same as
IF(SUM(date(updated_at) = date(now())), 1, 0)
Then UNION ALL result from other tables with the similar query.
You could try something like the following (untested):
select '#x' as table_name,
(select count(*) FROM #x and date(created_at) = date(now())) as created_rows,
(select count(*) FROM #x and date(updated_at) = date(now())) as updated_rows
As part of a prepared statement (https://dev.mysql.com/doc/refman/5.6/en/sql-syntax-prepared-statements.html) or proc (https://medium.com/#peter.lafferty/mysql-stored-procedures-101-6b4fe230967) or just union to get multiple tables (note that you'd have to change some syntax). I'm not sure how you need to run this, so I'm not sure what exactly you need, what kind of performance you're after, if you're just going to make a script and manually select the tables or if you're trying to run this on all tables, etc.
EDIT: jxc's query is much better than mine!
Is this what you are looking for? If so, make sure to change the table names in the subsequent UNION ALL queries.
SELECT
'accounts' AS TableName,
SUM(DATE(updated_at) = DATE(NOW())) AS updated_rows,
SUM(DATE(created_at) = DATE(NOW())) AS created_rows,
DATE(NOW()) AS `Date`
FROM
accounts
UNION ALL
SELECT
'accounts2' AS TableName,
SUM(DATE(updated_at) = DATE(NOW())) AS updated_rows,
SUM(DATE(created_at) = DATE(NOW())) AS created_rows,
DATE(NOW()) AS `Date`
FROM
accounts2
and so forth....
EDIT This query is identical to jxc's query posted earlier
I have a table:
QUOTE
| id | value | mar_id | date |
And I am trying to select the latest row for each mar_id (market id). I have managed to achieve what I need from the query below:
SELECT
q.*
FROM quote q
WHERE q.date = (
SELECT MAX(q1.date)
FROM quote q1
WHERE q.mar_id = q1.mar_id
)
However I find that the query is incredibly slow (>60s), to the extent my database kills the connection.
I did an EXPLAIN to find out why and got the result:
I have a composite unique index QUO_UQ on mar_id, date which appears to be getting used.
logically this doesn't seem such a tough query to run, what can I do to do this more efficiently?
An example of an uncorrelated subquery
SELECT x.*
FROM quote x
JOIN
( SELECT mar_id
, MAX(date) date
FROM quote
GROUP
BY mar_id
) y
ON y.mar_id = x.mar_id
AND y.date = x.date;
select * from (
select mar_id, [date],row_number() over (partition by mar_id order by [date] desc ) as [Rank] from
qoute
group by mar_id, [date]) q where Rank = 1
Your query is fine:
SELECT q.*
FROM quote q
WHERE q.date = (SELECT MAX(q1.date)
FROM quote q1
WHERE q.mar_id = q1.mar_id
);
I recommend an index on quote(mar_id, date). This is probably the fastest method to get your result.
EDIT:
I'm curious if you find that this uses the index:
SELECT q.*
FROM quote q
WHERE q.date = (SELECT q1.date
FROM quote q1
WHERE q.mar_id = q1.mar_id
ORDER BY q1.date DESC
LIMIT 1
);
I have a donation database and one of the reports I run against it I would like to include the number of donations that equal the months maximum donation. For example the months highest donation may be $100, but there may be 5 people who all donated $100, I would like to get that count.
My current query is:
SELECT SUM(mc_gross) AS Donations,
SUM(mc_fee) AS Fees,
COUNT(payment_date) AS DontationCount,
COUNT(DISTINCT payer_email) AS DonatorCount,
MAX(mc_gross) AS MaxDonation,
#MaxD:=MAX(mc_gross),
(
SELECT COUNT(*)
FROM #__paypal_donations
WHERE MONTH(payment_date) = MONTH(CURDATE())
AND YEAR(payment_date) = YEAR(CURDATE())
AND mc_gross = #MaxD
) as MaxDonationMultiplier,
AVG(mc_gross) AS AverageDonation
FROM #__paypal_donations
WHERE MONTH(payment_date) = MONTH(CURDATE())
AND YEAR(payment_date) = YEAR(CURDATE())
So I think I may be close, but it looks like either the value I am storing in #MaxD for use in my subquery is not working or the comparison itself in mc_gross = #MaxD is not working because if I replace #MaxD with a real value I get a proper count.
You cannot depend on the order of assignment of expressions in MySQL. That makes a query such as yours quite dangerous. Fortunately, you can easily solve this problem with a correlated subquery:
SELECT SUM(mc_gross) AS Donations, SUM(mc_fee) AS Fees, COUNT(payment_date) AS DontationCount,
COUNT(DISTINCT payer_email) AS DonatorCount, MAX(mc_gross) AS MaxDonation,
(SELECT COUNT(*)
FROM #__paypal_donations pd2
WHERE MONTH(pd2payment_date) = MONTH(pd.payment_date)) AND
YEAR(pd2payment_date) = YEAR(pd.payment_date) AND
pd2.mc_gross = MAX(mc_gross)
) as MaxDonationMultiplier,
AVG(mc_gross) AS AverageDonation
FROM #__paypal_donations pd
WHERE MONTH(payment_date) = MONTH(CURDATE()) AND
YEAR(payment_date) = YEAR(CURDATE());
Guys I have a query that will calculate a certain sum, same logic, same code, but with different where clause, Can I do this in one query? Example:
Select SUM( mi.myitem_price * msi.my_item_quantity) as order_sum from sometable where mi.myitem_order_id = 'somevalue';
2nd query:
Select SUM( mi.myitem_price * msi.my_item_quantity) as location_sum from sometable where mi.myitem_location_id = 'somevalue';
To make this clear, That first query will calculate all orders in different locations, while that 2nd query will calculate per location only. I need to get order_sum and location_sum simultaneously with different values.
You can try to do it this way (with conditional aggregation)
SELECT SUM(CASE WHEN mi.myitem_order_id = 'somevalue'
THEN mi.myitem_price * msi.my_item_quantity END) order_sum,
SUM(CASE WHEN mi.myitem_location_id = 'somevalue'
THEN mi.myitem_price * msi.my_item_quantity END) location_sum
-- WHERE mi.myitem_order_id = 'somevalue'
-- OR mi.myitem_location_id = 'somevalue'
FROM sometable
You can use the query like this also
SELECT (Select SUM( mi.myitem_price * msi.my_item_quantity)
from sometable AS mi where mi.myitem_order_id = 'somevalue') as order_sum,
(Select SUM( mi.myitem_price * msi.my_item_quantity)
from sometable AS mi where mi.myitem_location_id = 'somevalue') as location_sum