I have a query that gets me statistics about data in a table:
SELECT COUNT(1) batch_count,
ROUND(MAX(batch_duration_milliseconds) / 1000) max_batch_duration_seconds,
ROUND(AVG(batch_duration_milliseconds) / 1000) avg_batch_duration_seconds,
ROUND(MIN(batch_duration_milliseconds) / 1000, 1) min_batch_duration_seconds,
ROUND(SUM(total_queries_duration_milliseconds) / SUM(batch_duration_milliseconds) *
100) query_duration_percentage,
SUM(total_queries) queries,
ROUND(SUM(monitoring_queries) / SUM(total_queries) * 100) percent_monitoring_queries,
ROUND(SUM(total_queries) / SUM(batch_duration_milliseconds) * 1000) queries_per_second,
MAX(max_query_duration_milliseconds) max_query_duration,
ROUND(AVG(avg_query_duration_milliseconds)) avg_query_duration,
ROUND(SUM(retrieved_data_bytes) / 1024 / 1024) data_mb,
ROUND(MAX(retrieved_data_bytes) / 1024 / 1024) max_batch_data_mb,
ROUND(ROUND(AVG(retrieved_data_bytes)) / 1024 / 1024) avg_batch_data_mb,
ROUND(SUM(retrieved_data_bytes_calculation_duration_milliseconds) / SUM(batch_duration_milliseconds) *
100, 1) percent_data_calc_duration
FROM entrypoint_sql_queries
WHERE created_at > SUBDATE(NOW(), INTERVAL 1 HOUR);
now I want to add one more column that gets me the percent of queries caused by the entrypoint with the most queries:
SELECT ROUND(MAX(total_queries) / SUM(total_queries) * 100) percent_most_expensive_job_queries
FROM (
SELECT SUM(total_queries) AS total_queries
FROM entrypoint_sql_queries
WHERE created_at > SUBDATE(NOW(), INTERVAL 1 HOUR)
GROUP BY entrypoint
) as t;
How can I get both into the same resultset (i.e. I want to show the resulting number of query 2 as an additional column in query 1)
You can do it by adding your second query as a single column on the global select :
SELECT COUNT(1) batch_count,
ROUND(MAX(batch_duration_milliseconds) / 1000) max_batch_duration_seconds,
ROUND(AVG(batch_duration_milliseconds) / 1000) avg_batch_duration_seconds,
ROUND(MIN(batch_duration_milliseconds) / 1000, 1) min_batch_duration_seconds,
ROUND(SUM(total_queries_duration_milliseconds) / SUM(batch_duration_milliseconds) *
100) query_duration_percentage,
SUM(total_queries) queries,
ROUND(SUM(monitoring_queries) / SUM(total_queries) * 100) percent_monitoring_queries,
ROUND(SUM(total_queries) / SUM(batch_duration_milliseconds) * 1000) queries_per_second,
MAX(max_query_duration_milliseconds) max_query_duration,
ROUND(AVG(avg_query_duration_milliseconds)) avg_query_duration,
ROUND(SUM(retrieved_data_bytes) / 1024 / 1024) data_mb,
ROUND(MAX(retrieved_data_bytes) / 1024 / 1024) max_batch_data_mb,
ROUND(ROUND(AVG(retrieved_data_bytes)) / 1024 / 1024) avg_batch_data_mb,
ROUND(SUM(retrieved_data_bytes_calculation_duration_milliseconds) / SUM(batch_duration_milliseconds) *
100, 1) percent_data_calc_duration,
( SELECT ROUND(MAX(total_queries) / SUM(total_queries) * 100)
FROM (
SELECT SUM(total_queries) AS total_queries
FROM entrypoint_sql_queries
WHERE created_at > SUBDATE(NOW(), INTERVAL 1 HOUR)
GROUP BY entrypoint
) as t ) as percent_most_expensive_job_queries
FROM entrypoint_sql_queries
WHERE created_at > SUBDATE(NOW(), INTERVAL 1 HOUR);
Related
I have this code that works in MySQL
CASE
WHEN e.sex = 'M' THEN ROUND((salary / 30 * absents) + (salary / 30 / 8 * short_hours * 1.5))
ELSE ROUND((salary / 30 * absents) + (salary / 30 / 5 * short_hours * 1.5))
END AS deductions,
CASE
WHEN e.sex = 'M' THEN ROUND(salary / 30 / 8 * overtime * 1.5)
ELSE ROUND(salary / 30 / 5 * overtime * 1.5)
END AS overtime_bonus,
ROUND(salary - (SELECT deductions) + (SELECT overtime_bonus)) AS total_salary,
I am trying to make it work in SQLite but I get the error no such column: deductions
My question is: can I use a calculated field in a subquery in SQLite3?
I have a query that selects all locations within a 30 mile radius of a GPS pt. I want to get the id's of those locations, but it's also returning the distance from the center pt.
Is there anyway to perform the distance calculation without returning it?
Query:
SELECT id, 3956 * 2 * ASIN(SQRT(
POWER(SIN((34.1844709 - abs(dest.lat)) * pi()/180 / 2),
2) + COS(37.7749295 * pi()/180 ) * COS(abs(dest.lat) *
pi()/180) * POWER(SIN((-118.131809 - dest.lng) *
pi()/180 / 2), 2) )) as distance
FROM location dest
having distance < 30
ORDER by distance
LIMIT 30
Output:
---------------------------
id | distance
---------------------------
1 | 2.310
2 | 2.356
17 | 4.298
Query based off:
http://www.notaires.fr/sites/default/files/geo_searchjkkjkj_0.pdf
Can you just do another select on this?
Select id
From (SELECT id, 3956 * 2 * ASIN(SQRT(
POWER(SIN((34.1844709 - abs(dest.lat)) * pi()/180 / 2),
2) + COS(37.7749295 * pi()/180 ) * COS(abs(dest.lat) *
pi()/180) * POWER(SIN((-118.131809 - dest.lng) *
pi()/180 / 2), 2) )) as distance
FROM location dest
having distance < 30
ORDER by distance
LIMIT 30) dst
I recently posted this question: getting distance between two points using google gps api with php
and I received a reply which was awesome however I had some questions about the person's response and due to my low/new status to this site I wasn't able to comment back. In response to my question the other user posted this query
**
SELECT a.*, 3956 * 2 * ASIN(SQRT( POWER(SIN(($lat - lat) * pi()/180 / 2), 2) + COS($lat * pi()/180) * COS(lat * pi()/180) *
POWER(SIN(($long - longi) * pi()/180 / 2), 2) )) as distance
FROM table
GROUP BY id HAVING distance <= 500
ORDER by distance ASC**
I had a few questions about this query and was hoping somebody could help.
1.What is the a.*? I'm not super advanced in sql but pretty efficient and have never seen something like this. I don't know if it is supposed to represent an arbritrary field or an actual field in my table
2.Since I'm doing this in php the query will go in quotes which will then make this query syntax slightly different. I was wondering if someone knew what the query would look like in quotes.
3.There is also "GROUP BY id" in this query. I do have an id field in my table that I'm querying from. Is this "id" associated with my id field in my table?
any help would be awesome.
Your query should be like this :-
a is table alias here.
SELECT a.*, 3956 * 2 * ASIN(SQRT( POWER(SIN(($lat - lat) * pi()/180 / 2), 2) + COS($lat * pi()/180) * COS(lat * pi()/180) *
POWER(SIN(($long - longi) * pi()/180 / 2), 2) )) as distance
FROM table AS a
GROUP BY id HAVING distance <= 500
ORDER by distance ASC
Or you can use simple
SELECT *, 3956 * 2 * ASIN(SQRT( POWER(SIN(($lat - lat) * pi()/180 / 2), 2) + COS($lat * pi()/180) * COS(lat * pi()/180) *
POWER(SIN(($long - longi) * pi()/180 / 2), 2) )) as distance
FROM table
GROUP BY id HAVING distance <= 500
ORDER by distance ASC
I'm using a slightly-modified version of the geocoded gem which returns this query when I call near on my model (calling Deal.near(southwest), where southwest is an array of geo coordinates):
SELECT
deals.*,
3958.755864232 * 2 * ASIN(SQRT(POWER(SIN((37.772476604436974 - addresses.lat) * PI() / 180 / 2), 2) + COS(37.772476604436974 * PI() / 180) * COS(addresses.lat * PI() / 180) * POWER(SIN((-122.42336332798004 - addresses.lng) * PI() / 180 / 2), 2) )) AS distance,
CAST(DEGREES(ATAN2( RADIANS(addresses.lng - -122.42336332798004), RADIANS(addresses.lat - 37.772476604436974))) + 360 AS decimal) % 360 AS bearing
FROM "deals"
INNER JOIN "companies" ON "companies"."id" = "deals"."company_id"
INNER JOIN "addresses" ON "addresses"."addressable_id" = "companies"."id" AND "addresses"."addressable_type" = 'Company'
WHERE (
addresses.lat BETWEEN 37.483013038215276 AND 38.06194017065867
AND addresses.lng BETWEEN -122.78956461309022 AND -122.05716204286986
)
GROUP BY
deals.id,
deals.created_at,
deals.updated_at,
deals.active,
deals.company_id,
deals.title,
deals.limitations,
deals.redemption_count,
addresses.lat,
addresses.lng
HAVING 3958.755864232 * 2 * ASIN(SQRT(POWER(SIN((37.772476604436974 - addresses.lat) * PI() / 180 / 2), 2) + COS(37.772476604436974 * PI() / 180) * COS(addresses.lat * PI() / 180) * POWER(SIN((-122.42336332798004 - addresses.lng) * PI() / 180 / 2), 2) )) <= 20
ORDER BY 3958.755864232 * 2 * ASIN(SQRT(POWER(SIN((37.772476604436974 - addresses.lat) * PI() / 180 / 2), 2) + COS(37.772476604436974 * PI() / 180) * COS(addresses.lat * PI() / 180) * POWER(SIN((-122.42336332798004 - addresses.lng) * PI() / 180 / 2), 2) )) ASC
My issue is that this will return multiple Deal records if that Deal's company has multiple Addresses, which I don't want.
In MySQL, I could just omit address.lat, address.lng in the GROUP_BY clause and it will properly group the records, but I can't do this in PostgreSQL.
I know I could wrap the whole query above in another SELECT and GROUP_BY, like this:
SELECT
id, created_at, updated_at, active, title, punches_to_complete, company_id, description, lat, lng, MIN(distance), bearing
FROM ( ... ) AS t
GROUP BY company_id
... where the ellipsis is the query from above. That (I believe) should get me the desired result in both MySQL and PostgreSQL.
The only problem is that I have no idea how to write this in ARel!
I had tried the following, a la this tip from the ARel guru, but I couldn't really make it work quite right (calling to_sql as the OP had said fixed his issue escapes the quotes, which freaks PostgreSQL out).
Can anyone help me with this???
UPDATE:
I've managed to get this done with an additional scope, like so:
scope :nearest, lambda { |coords|
subquery = "(#{Deal.near(coords).to_sql}) AS t1"
columns = Deal.columns.map{ |c| c.name }.join(',')
Deal.select(columns)
.select('MIN(distance) AS distance')
.from(subquery)
.group(columns)
.order('distance ASC')
}
However, this totally breaks chainability, as now I cannot call something like current_user.deals.nearest(coords), since that tags on an additional WHERE deals.user_id = 1 to the query outside of the subselect. I tried compensating for this by moving this logic into a class method and blanking the wheres clause on the SelectManager manually, like this:
def self.nearest(coords)
subquery = "(#{Deal.near(coords).to_sql}) AS t1"
columns = Deal.columns.map{ |c| c.name }.join(',')
query = Deal.select(columns)
.select('MIN(distance) AS distance')
.from(subquery)
.group(columns)
.order('distance ASC')
query.arel.ast.cores[0].wheres = []
query
end
... but that doesn't seem to work either: the additional WHERE clause is still appended:
Failure/Error:
#user.deals.nearest(southwest).first.distance.to_f.round(2).should ==
ActiveRecord::StatementInvalid:
Mysql2::Error: Unknown column 'deals.user_id' in 'where
clause': SELECT id,created_at,updated_at,user_id,company_id,
MIN(distance) AS distance FROM (SELECT deals.*, 3958.755864232 * 2 *
ASIN(SQRT(POWER(SIN((37.772476604436974 - addresses.lat) * PI() / 180
/ 2), 2) + COS(37.772476604436974 * PI() / 180) * COS(addresses.lat *
PI() / 180) * POWER(SIN((-122.42336332798004 - addresses.lng) * PI() /
180 / 2), 2) )) AS distance, CAST(DEGREES(ATAN2( RADIANS(addresses.lng
- -122.42336332798004), RADIANS(addresses.lat - 37.772476604436974)))
+ 360 AS decimal) % 360 AS bearing FROM deals INNER JOIN companies
ON companies.id = deals.company_id INNER JOIN addresses ON
addresses.addressable_id = companies.id AND
addresses.addressable_type = 'Company' WHERE deals.user_id =
26 AND (addresses.lat BETWEEN 37.483013038215276 AND 38.06194017065867
AND addresses.lng BETWEEN -122.78956461309022 AND -122.05716204286986)
GROUP BY
deals.id,deals.created_at,deals.updated_at,deals.user_id,deals.company_id,
addresses.lat, addresses.lng HAVING 3958.755864232 * 2 *
ASIN(SQRT(POWER(SIN((37.772476604436974 - addresses.lat) * PI() / 180
/ 2), 2) + COS(37.772476604436974 * PI() / 180) * COS(addresses.lat *
PI() / 180) * POWER(SIN((-122.42336332798004 - addresses.lng) * PI() /
180 / 2), 2) )) <= 20 ORDER BY 3958.755864232 * 2 *
ASIN(SQRT(POWER(SIN((37.772476604436974 - addresses.lat) * PI() / 180
/ 2), 2) + COS(37.772476604436974 * PI() / 180) * COS(addresses.lat *
PI() / 180) * POWER(SIN((-122.42336332798004 - addresses.lng) * PI() /
180 / 2), 2) )) ASC) AS t1 WHERE deals.user_id = 26 GROUP BY
id,created_at,updated_at,user_id,company_id ORDER BY distance ASC
LIMIT 1
Is what I'm trying to do even possible with ARel? The additional scopes above feel really dirty to me (parsing the subquery to raw SQL? I thought ARel was supposed to make it so I never did that!)
Related question: Can ARel formulate cross-db queries for CTEs (Common Table Expressions)?
I have the next query for getting addresses within a given distance and given postal code. Distance is calculated, based upon longitude and latitude data.
In this example i have replaced the user-input for just values (lat=52.64, long=6.88 en desired distance=10km)
the query:
SELECT *,
ROUND( SQRT( POW( ( (69.1/1.61) * ('52.64' - latitude)), 2) + POW(( (53/1.61) * ('6.88' - longitude)), 2)), 1) AS distance
FROM lp_relations_addresses distance
WHERE distance < 10
ORDER BY `distance` DESC
gives unknown column distance as error message.
when leaving out the where clausule i get every record of the table including their calculated distance. In this case i have to fetch the whole table.
How do i get only the desired records to fetch??
Thanks in advance for any comment!
You can't reference an alias in the select clause from another part of the sql statement. You need to put the whole expression in your where clause:
WHERE
ROUND( SQRT( POW( ( (69.1/1.61) * ('52.64' - latitude)), 2)
+ POW(( (53/1.61) * ('6.88' - longitude)), 2)), 1) < 10
A cleaner solution would be to use a sub-query to generate the calculated data:
SELECT *, distance
FROM (
SELECT *,
ROUND( SQRT( POW( ( (69.1/1.61) * ('52.64' - latitude)), 2)
+ POW(( (53/1.61) * ('6.88' - longitude)), 2)), 1) AS distance
FROM lp_relations_addresses
) d
WHERE d.distance < 10
ORDER BY d.distance DESC
Demo: http://www.sqlize.com/q96p2mCwnJ
As mellamokb notes, you can't reference column aliases in the WHERE clause. You can, however, do it in a HAVING clause:
SELECT *,
ROUND( SQRT( POW( ( (69.1/1.61) * ('52.64' - latitude)), 2) +
POW(( (53/1.61) * ('6.88' - longitude)), 2)), 1) AS distance
FROM lp_relations_addresses
HAVING distance < 10
ORDER BY distance DESC
Ps. If you have lots of addresses, you might want to consider optimizing the query by ruling out some of them early. For example, with suitable indexes, the following version might be considerably faster:
SELECT *,
ROUND( SQRT( POW( ( (69.1/1.61) * ('52.64' - latitude)), 2) +
POW(( (53/1.61) * ('6.88' - longitude)), 2)), 1) AS distance
FROM lp_relations_addresses
WHERE latitude > '52.64' - 10 / (69.1/1.61)
AND latitude < '52.64' + 10 / (69.1/1.61)
AND longitude > '6.88' - 10 / (53/1.61)
AND longitude < '6.88' + 10 / (53/1.61)
HAVING distance < 10
ORDER BY distance DESC
You are aliasing the calculation as 'distance', but you are also aliasing table 'lp_relations_addresses' as 'distance'. Try giving them a different name like this:
SELECT *,
ROUND( SQRT( POW( ( (69.1/1.61) * ('52.64' - latitude)), 2) + POW(( (53/1.61) * ('6.88' - longitude)), 2)), 1) AS distance
FROM lp_relations_addresses addr
WHERE distance < 10
ORDER BY `distance` DESC