Reusing calculations in an SQL query - mysql

Is it possible to simplify this sql query? Lots of calculations are reused and it would be nice to name each expression and use the name instead of the full expression.
SELECT SUM(T2.price * T1.amount) As price,
(SUM(T2.price * T1.amount) - (SUM(T2.price * T1.amount) * (T3.discount / 100))) As base_price,
((SUM(T2.price * T1.amount) - (SUM(T2.price * T1.amount) * (T3.discount / 100))) * (T3.vat / 100)) As vat_amount,
(((SUM(T2.price * T1.amount) - (SUM(T2.price * T1.amount) * (T3.discount / 100))) * (T3.vat / 100)) + (SUM(T2.price * T1.amount) - (SUM(T2.price * T1.amount) * (T3.discount / 100)))) As total_price
I'm looking for a solution similar to this:
SELECT SUM(T2.price * T1.amount) As price,
(price - (price * (T3.discount / 100))) As base_price,
(base_price) * (T3.vat / 100)) As vat_amount,
(vat_amount) + (base_price) As total_price

I tried doing it with CTE's and subqueries as suggested but it ended up looking pretty complicated and undreadable so I decided to just do the calculations outside of sql (php in this case)

Maybe something like that:
select
(A.price - (A.price * B.p_discount)) As base_price,
((A.price - (A.price * B.p_discount)) * B.p_vat) As vat_amount,
(((A.price - (A.price * B.p_discount)) * B.p_vat) + (A.price - (A.price * B.p_discount))) As total_price
from
( select SUM(T2.price * T1.amount) As price, ...
from T1 join T2 on ....
) A
join
(select (T3.discount / 100) as p_discount , (T3.vat / 100) as p_vat ,*
from T3
) B on ...

Related

How can I do an inner join inside of a subquery? MySQL

I am trying to adapt this select statement to my tables. This query selects all of the zip codes from a zip code table that are within a certain distance of a given zip code. However, I have a Salon table that stores the zip codes as a foreign from the zip code table. I need to select all of the salons within a certain distance of the given zip code.
DELIMITER //
CREATE PROCEDURE zip_search(lat_param FLOAT(9,6),
long_param FLOAT(9,6), dist_param INT(100)
)
BEGIN
SELECT zipcode
FROM (
SELECT z.zipcode, z.latitude, z.longitude,
p.radius,
p.distance_unit
* DEGREES(ACOS(COS(RADIANS(p.latpoint))
* COS(RADIANS(z.latitude))
* COS(RADIANS(p.longpoint - z.longitude))
+ SIN(RADIANS(p.latpoint))
* SIN(RADIANS(z.latitude)))) AS distance
FROM accounts_zip AS z
JOIN ( /* these are the query parameters */
SELECT lat_param AS latpoint, long_param AS longpoint,
dist_param AS radius, 69.0 AS distance_unit
) AS p ON 1=1
WHERE z.latitude
BETWEEN p.latpoint - (p.radius / p.distance_unit)
AND p.latpoint + (p.radius / p.distance_unit)
AND z.longitude
BETWEEN p.longpoint - (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
AND p.longpoint + (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
) AS d
WHERE distance <= radius
ORDER BY distance;
END //
I tried adding an inner join after selecting from the salon table, however I do not understand sub queries and joins very well. Any thoughts would be greatly appreciated.
DELIMITER //
CREATE PROCEDURE zip_search(lat_param FLOAT(9,6),
long_param FLOAT(9,6), dist_param INT(100)
)
BEGIN
SELECT *
FROM (
SELECT z.zipcode, z.latitude, z.longitude,
p.radius,
p.distance_unit
* DEGREES(ACOS(COS(RADIANS(p.latpoint))
* COS(RADIANS(z.latitude))
* COS(RADIANS(p.longpoint - z.longitude))
+ SIN(RADIANS(p.latpoint))
* SIN(RADIANS(z.latitude)))) AS distance
FROM accounts_salon INNER JOIN accounts_zip ON accounts_salon.id=accounts_zip.id AS z
JOIN ( /* these are the query parameters */
SELECT lat_param AS latpoint, long_param AS longpoint,
dist_param AS radius, 69.0 AS distance_unit
) AS p ON 1=1
WHERE z.latitude
BETWEEN p.latpoint - (p.radius / p.distance_unit)
AND p.latpoint + (p.radius / p.distance_unit)
AND z.longitude
BETWEEN p.longpoint - (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
AND p.longpoint + (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
) AS d
WHERE distance <= radius
ORDER BY distance;
END //
I don't think the ' AS z' in first inner join is necessary, if you try to get the join result of tables accounts_salon and accounts_zip, and treat it as the left table of second inner join, then just continure write the next inner join.

How do I use raw select in Eloquent?

My SQL is something like this:
SELECT
NAME,
PLACE,
ETC,
format(((sum((AMOUNT * U_PRICE)) - ((sum((AMOUNT * U_PRICE)) * 20) / 100)) * 1.18),2) AS TOTAL_PRICE
FROM
THE_TABLE
And my Eloquent query is:
theTable::select(
'NAME',
'PLACE',
'ETC',
'format(((sum((AMOUNT * U_PRICE)) - ((sum((AMOUNT * U_PRICE)) * 20) / 100)) * 1.18),2) AS TOTAL_PRICE'
)
->get();
But it doesn't work because Eloquent interprets it something like select name, place, etc, format sum from the_table.
I have treid to use DB::raw but then it says that it didn't expect object.
theTable::select(
'NAME',
'PLACE',
'ETC',
DB::raw('format(((sum((AMOUNT * U_PRICE)) - ((sum((AMOUNT * U_PRICE)) * 20) / 100)) * 1.18),2) AS TOTAL_PRICE')
)
->get();
So, how can I use raw SELECT statement in Eloquent?
Use this way
DB::table('THE_TABLE')
->select(DB::raw('
NAME,
PLACE,
ETC,
format(((sum((AMOUNT * U_PRICE)) - ((sum((AMOUNT * U_PRICE)) * 20) / 100)) * 1.18),2) AS TOTAL_PRICE
'))
->get();

Perform query on a calculated column using Laravel

I want to perform the following SQL statement in PHP using Laravel's Eloquent model:
SELECT *, (3959 * acos(cos(radians(37)) * cos(radians(lat)) * cos(radians(lng) - radians(-122)) + sin(radians(37)) * sin(radians(lat )))) AS distance
FROM example_retailers
HAVING distance < 1000
ORDER BY distance
OFFSET 4
LIMIT 3;
I am struggling to translate this into a Laravel's Eloquent Model (mainly the calculated column).
This is what I have so far:
ExampleRetailer::
// TODO: add calculated row
where('distance', '<', 100)
->orderBy('distance')
->skip(4)
->limit(3)
->get();
You can use raw queries (source )
DB::table('example_retailers')
->select(DB::raw('*, (3959 * acos(cos(radians(37)) * cos(radians(lat)) * cos(radians(lng) - radians(-122)) + sin(radians(37)) * sin(radians(lat )))) AS distance'))
->having('distance', '<', 100)
->orderBy('distance')
->skip(4)
->limit(3)
->get();
If it's only to make a new row using SQL:
SELECT *, CASE WHEN (3959 * acos(cos(radians(37))
* cos(radians(lat))
* cos(radians(lng) - radians(-122))
+ sin(radians(37))
* sin(radians(lat )))) < 100 THEN END AS NewRow
FROM example_retailers

MySQL column not found despite being declared?

This query works:
select d.*,
(6371 * 3.1415926 * SQRT((:lat - dl.lat) * (:lat - dl.lat) + COS(:lat / 57.2957795) * COS(dl.lat / 57.2957795) * (:long - dl.long) * (:long - dl.long)) / 180) as xdistance
from `dudes` as d
left join `dude_locations` as dl on (dl.id_dude = d.id)
where
(6371 * 3.1415926 * SQRT((:lat - dl.lat) * (:lat - dl.lat) + COS(:lat / 57.2957795) * COS(dl.lat / 57.2957795) * (:long - dl.long) * (:long - dl.long)) / 180) <= dl.distance
group by d.id
limit 20
However, this query throws a "column xdistance not found" error:
select d.*,
(6371 * 3.1415926 * SQRT((:lat - dl.lat) * (:lat - dl.lat) + COS(:lat / 57.2957795) * COS(dl.lat / 57.2957795) * (:long - dl.long) * (:long - dl.long)) / 180) as xdistance
from `dudes` as d
left join `dude_locations` as dl on (dl.id_dude = d.id)
where
xdistance <= dl.distance
group by d.id
limit 20
All I'm trying to do is make it so the same calculation isn't made twice. Is this possible?
Any help would be appreciated.
where
d.xdistance <= dl.distance
It is possible that the query you want is something like this:
select d.*,
MIN((6371 * 3.1415926 * SQRT((:lat - dl.lat) * (:lat - dl.lat) + COS(:lat / 57.2957795) * COS(dl.lat / 57.2957795) * (:long - dl.long) * (:long - dl.long)) / 180) ) as xdistance
from `dudes` as d left join
`dude_locations` as dl
on (dl.id_dude = d.id)
group by d.id
having xdistance <= min(dl.distance)
limit 20;

MySQL using `IN` while having multiple values

I am trying to count of coupons sold by each store from the list of stores within 20 miles range. I know the following syntax will work if there is only 1 store.
SELECT sum(couponscount) as count where restaurant IN (SELECT storename where bhal bhal bhal and output is one value)
What is I the IN (SELECTstorenamewhere bhal bhal bhal and output is multiple values) will return multiple values?
Like in my case the complete SQL is like and its not working
SELECT sum(couponscount) as count FROM `coupons` having `restaurant` IN (SELECT `storename`, ((ACOS(SIN(-27.561264299999998 * PI()/180) * SIN(latitude * PI()/180) + COS(-27.561264299999998 * PI()/180) * COS(latitude * PI()/180) * COS((153.07304890000003 – longitude) * PI()/180)) *180 / PI( )) *60 * 1.1515) AS `distance` FROM `stores` WHERE `status`=’active’ HAVING `distance` <=20)
Is there anyway to make it working?
SELECT sum(couponscount) AS COUNT,restaurant
FROM `coupons`
WHERE `restaurant` IN
(SELECT `storename`
FROM `stores`
WHERE `status`='active'
AND
((ACOS(SIN(-27.561264299999998 * PI()/180) * SIN(latitude * PI()/180) + COS(-27.561264299999998 * PI()/180) * COS(latitude * PI()/180) * COS((153.07304890000003 – longitude) * PI()/180)) *180 / PI()) *60 * 1.1515) <=20)
GROUP BY restaurant
Also use proper quotes for active.
Presumably, you want to get the count of coupons from stores within a distance of 20. Moving the having condition to a where clause should do what you want:
SELECT sum(couponscount) as count
FROM `coupons`
WHERE `restaurant` IN (SELECT `storename`
FROM `stores`
WHERE `status` = 'active' AND
((ACOS(SIN(-27.561264299999998 * PI()/180) * SIN(latitude * PI()/180) + COS(-27.561264299999998 * PI()/180) * COS(latitude * PI()/180) * COS((153.07304890000003 – longitude) * PI()/180)) *180 / PI( )) *60 * 1.1515) <= 20
);
You had a major syntax problem because your subquery returned two columns. When you use a subquery with in, you can only return one column, in this case, storename. I moved the code for the distance calculation to the where clause. No having clause is needed either in the subquery or the outer query.