I was hoping someone could point me in the right direction as to why my data is not ordered by the flight_count column?
SELECT pilot_id,
pilot_firstname,
pilot_lastname,
pilot_email,
licence_num,
flight_count
FROM pilots
INNER JOIN
( SELECT pilot_id,
COUNT(flight_id) AS 'flight_count'
FROM flights
GROUP BY pilot_id
ORDER BY flight_count DESC
) as a
USING (pilot_id);
Move the ordering criteria to the outer select:
SELECT p.pilot_id, p.pilot_firstname, p.pilot_lastname, p.pilot_email, p.licence_num,
fc.flight_count
FROM pilots p
JOIN (
SELECT pilot_id, COUNT(flight_id) AS flight_count
FROM flights
GROUP BY pilot_id
) as fc
on fc.pilot_id = p.pilot_id
ORDER BY fc.flight_count DESC;
Note you should not be 'quoting' column aliases to delimit them, and the name is fine as-is; it's also generally a good idea to use (meaningful) table aliases explicitely, it helps with readability and also means there's less work for the query optimizer to do if columns are explicitely aliased.
Related
I have a mysql query to join four tables and I thought that it was just best to join tables but now that mysql data is getting bigger the query seems to cause the application to stop execution.
SELECT
`purchase_order`.`id`,
`purchase_order`.`po_date` AS po_date,
`purchase_order`.`po_number`,
`purchase_order`.`customer_id` AS customer_id ,
`customer`.`name` AS customer_name,
`purchase_order`.`status` AS po_status,
`purchase_order_items`.`product_id`,
`purchase_order_items`.`po_item_name`,
`product`.`weight` as product_weight,
`product`.`pending` as product_pending,
`product`.`company_owner` as company_owner,
`purchase_order_items`.`uom`,
`purchase_order_items`.`po_item_type`,
`purchase_order_items`.`order_sequence`,
`purchase_order_items`.`pending_balance`,
`purchase_order_items`.`quantity`,
`purchase_order_items`.`notes`,
`purchase_order_items`.`status` AS po_item_status,
`purchase_order_items`.`id` AS po_item_id
FROM `purchase_order`
INNER JOIN customer ON `customer`.`id` = `purchase_order`.`customer_id`
INNER JOIN purchase_order_items ON `purchase_order_items`.`po_id` = `purchase_order`.`id`
INNER JOIN product ON `purchase_order_items`.`product_id` = `product`.`id`
GROUP BY id ORDER BY `purchase_order`.`po_date` DESC LIMIT 0, 20
my problem really is the query that takes a lot of time to finish. Is there a way to speed this query or to change this query for faster retrieval of the data?
heres the EXPLAIN EXTENED as requested in the comments.
Thanks in advance, I really hope this is the right channel for me to ask. If not please let me know.
Will this give you the correct list of ids?
SELECT id
FROM purchase_order
ORDER BY`po_date` DESC
LIMIT 0, 20
If so, then start with that before launching into the JOIN. You can also (I think) get rid of the GROUP BY that is causing an "explode-implode" of rows.
SELECT ...
FROM ( SELECT id ... (as above) ...) AS ids
JOIN purchase_order po ON po.id = ids.id
JOIN ... (the other tables)
GROUP BY ... -- (this may be problematic, especially with the LIMIT)
ORDER BY po.po_date DESC -- yes, this needs repeating
-- no LIMIT
Something like this
SELECT
`purchase_order`.`id`,
`purchase_order`.`po_date` AS po_date,
`purchase_order`.`po_number`,
`purchase_order`.`customer_id` AS customer_id ,
`customer`.`name` AS customer_name,
`purchase_order`.`status` AS po_status,
`purchase_order_items`.`product_id`,
`purchase_order_items`.`po_item_name`,
`product`.`weight` as product_weight,
`product`.`pending` as product_pending,
`product`.`company_owner` as company_owner,
`purchase_order_items`.`uom`,
`purchase_order_items`.`po_item_type`,
`purchase_order_items`.`order_sequence`,
`purchase_order_items`.`pending_balance`,
`purchase_order_items`.`quantity`,
`purchase_order_items`.`notes`,
`purchase_order_items`.`status` AS po_item_status,
`purchase_order_items`.`id` AS po_item_id
FROM (SELECT id, po_date, po_number, customer_id, status
FROM purchase_order
ORDER BY `po_date` DESC
LIMIT 0, 5) as purchase_order
INNER JOIN customer ON `customer`.`id` = `purchase_order`.`customer_id`
INNER JOIN purchase_order_items
ON `purchase_order_items`.`po_id` = `purchase_order`.`id`
INNER JOIN product ON `purchase_order_items`.`product_id` = `product`.`id`
GROUP BY purchase_order.id DESC
LIMIT 0, 5
You need to be sure that purchase_order.po_date and all id column are indexed. You can check it with below query.
SHOW INDEX FROM yourtable;
Since you mentioned that data is getting bigger. I would suggest doing sharding and then you can parallelize multiple queries. Please refer to the following article
Parallel Query for MySQL with Shard-Query
First, I cleaned up readability a bit. You don't need tick marks around every table.column reference. Also, for short-hand, using aliases works well. Ex: "po" instead of "purchase_order", "poi" instead of "purchase_order_items". The only time I would use tick marks is around reserved words that might cause a problem.
Second, you don't have any aggregations (sum, min, max, count, avg, etc.) in your query so you should be able to strip the GROUP BY clause.
As for indexes, I would have to assume you have an index on your reference tables on their respective "id" key columns.
For your Purchase Order table, I would have an index on that based on the "po_date" in the first index field position in case you already had an index using it. Since your Order by is on that, let the engine jump directly to those dated records first and you have your descending order resolved.
SELECT
po.id,
po.po_date,
po.po_number,
po.customer_id,
c.`name` AS customer_name,
po.`status` AS po_status,
poi.product_id,
poi.po_item_name,
p.weight as product_weight,
p.pending as product_pending,
p.company_owner,
poi.uom,
poi.po_item_type,
poi.order_sequence,
poi.pending_balance,
poi.quantity,
poi.notes,
poi.`status` AS po_item_status,
poi.id AS po_item_id
FROM
purchase_order po
INNER JOIN customer c
ON po.customer_id = c.id
INNER JOIN purchase_order_items poi
ON po.id = poi.po_id
INNER JOIN product p
ON poi.product_id = p.id
ORDER BY
po.po_date DESC
LIMIT
0, 20
I have query like below:
SELECT kd.id_karir, kd.nama, kd.kelamin,
(YEAR(NOW())-YEAR(tanggal)) usia, MAX(pf.jenis), pf.jenis,
pf.nama AS pendidikan, pf.jurusan, kd.alamat, kd.telepon,
kd.handphone, kd.email, kd.tempat AS tempat_lahir,
kd.tanggal AS tanggal_lahir
FROM keadaan_diri AS kd
LEFT OUTER JOIN pendidikan_formal AS pf ON (kd.id_karir = pf.id_karir)
WHERE kd.id_karir = 'P1409047'
GROUP BY kd.id_karir
ORDER BY kd.nama ASC, pf.jenis DESC
I mean to returning the last data on the table pendidikan_formal using max and group but the query doesn't work.
First of all, you can / should (depending on the MySQL configuration) only select and order by columns that are part of your group by clause. For all other columns, you have to specify an aggregation function. For example, let's say you have two records of humans, both have the same name and a different age. When you group by name, you have to choose one of the two age values (max, min, average, ...). If you don't care which, you could turn off sql mode only full group by. I wouldn't suggest that however.
In order to get the one record with some maximum value however, group by is not the right approach. Take a look at these examples:
Subselect:
SELECT name, age, ...
FROM humans
WHERE age=(SELECT MAX(age) FROM humans);
Order by and limit:
SELECT name, age, ...
FROM humans
ORDER BY age DESC
LIMIT 1;
Left join:
SELECT name, age, ...
FROM humans h1
LEFT JOIN humans h2 ON h1.age < h2.age
WHERE h2.age IS NULL;
Now if you want all maximum rows per group, check one of these answers with tag greatest-n-per-group.
You can use a correlated subquery. Your question is a bit vague; I assume that id_karir is the group and tanggal is the date.
If I understand correctly, this would apply to your query as:
SELECT kd.id_karir, kd.nama, kd.kelamin,
(YEAR(NOW())-YEAR(tanggal)) usia, pf.jenis, pf.jenis,
pf.nama AS pendidikan, pf.jurusan, kd.alamat, kd.telepon,
kd.handphone, kd.email, kd.tempat AS tempat_lahir,
kd.tanggal AS tanggal_lahir
FROM keadaan_diri kd LEFT OUTER JOIN
pendidikan_formal pf
ON kd.id_karir = pf.id_karir AND
pf.tanggal = (SELECT MAX(pf2.tanggal) FROM pendidikan_formal pf2 WHERE pf2.id_karir = pf.id_karir)
This is not an aggregation query. This is a filtering query.
I have SQL query with LEFT JOIN:
SELECT COUNT(stn.stocksId) AS count_stocks
FROM MedicalFacilities AS a
LEFT JOIN stocks stn ON
(stn.stocksIdMF = ( SELECT b.MedicalFacilitiesIdUser
FROM medicalfacilities AS b
WHERE b.MedicalFacilitiesIdUser = a.MedicalFacilitiesIdUser
ORDER BY stn.stocksId DESC LIMIT 1)
AND stn.stocksEndDate >= UNIX_TIMESTAMP() AND stn.stocksStartDate <= UNIX_TIMESTAMP())
These query I want to select one row from table stocks by conditions and with field equal value a.MedicalFacilitiesIdUser.
I get always count_stocks = 0 in result. But I need to get 1
The count(...) aggregate doesn't count null, so its argument matters:
COUNT(stn.stocksId)
Since stn is your right hand table, this will not count anything if the left join misses. You could use:
COUNT(*)
which counts every row, even if all its columns are null. Or a column from the left hand table (a) that is never null:
COUNT(a.ID)
Your subquery in the on looks very strange to me:
on stn.stocksIdMF = ( SELECT b.MedicalFacilitiesIdUser
FROM medicalfacilities AS b
WHERE b.MedicalFacilitiesIdUser = a.MedicalFacilitiesIdUser
ORDER BY stn.stocksId DESC LIMIT 1)
This is comparing MedicalFacilitiesIdUser to stocksIdMF. Admittedly, you have no sample data or data layouts, but the naming of the columns suggests that these are not the same thing. Perhaps you intend:
on stn.stocksIdMF = ( SELECT b.stocksId
-----------------------------^
FROM medicalfacilities AS b
WHERE b.MedicalFacilitiesIdUser = a.MedicalFacilitiesIdUser
ORDER BY b.stocksId DESC
LIMIT 1)
Also, ordering by stn.stocksid wouldn't do anything useful, because that would be coming from outside the subquery.
Your subquery seems redundant and main query is hard to read as much of the join statements could be placed in where clause. Additionally, original query might have a performance issue.
Recall WHERE is an implicit join and JOIN is an explicit join. Query optimizers
make no distinction between the two if they use same expressions but readability and maintainability is another thing to acknowledge.
Consider the revised version (notice I added a GROUP BY):
SELECT COUNT(stn.stocksId) AS count_stocks
FROM MedicalFacilities AS a
LEFT JOIN stocks stn ON stn.stocksIdMF = a.MedicalFacilitiesIdUser
WHERE stn.stocksEndDate >= UNIX_TIMESTAMP()
AND stn.stocksStartDate <= UNIX_TIMESTAMP()
GROUP BY stn.stocksId
ORDER BY stn.stocksId DESC
LIMIT 1
I have couple tables joined in MySQL - one has many others.
And try to select items from one, ordered by min values from another table.
Without grouping in seems to be like this:
Code:
select `catalog_products`.id
, `catalog_products`.alias
, `tmpKits`.`minPrice`
from `catalog_products`
left join `product_kits` on `product_kits`.`product_id` = `catalog_products`.`id`
left join (
SELECT MIN(new_price) AS minPrice, id FROM product_kits GROUP BY id
) AS tmpKits on `tmpKits`.`id` = `product_kits`.`id`
where `category_id` in ('62')
order by product_kits.new_price ASC
Result:
But when I add group by, I get this:
Code:
select `catalog_products`.id
, `catalog_products`.alias
, `tmpKits`.`minPrice`
from `catalog_products`
left join `product_kits` on `product_kits`.`product_id` = `catalog_products`.`id`
left join (
SELECT MIN(new_price) AS minPrice, id FROM product_kits GROUP BY id
) AS tmpKits on `tmpKits`.`id` = `product_kits`.`id`
where `category_id` in ('62')
group by `catalog_products`.`id`
order by product_kits.new_price ASC
Result:
And this is incorrect sorting!
Somehow when I group this results, I get id 280 before 281!
But I need to get:
281|1600.00
280|2340.00
So, grouping breaks existing ordering!
For one, when you apply the GROUP BY to only one column, there is no guarantee that the values in the other columns will be consistently correct. Unfortunately, MySQL allows this type of SELECT/GROUPing to happen other products don't. Two, the syntax of using an ORDER BY in a subquery while allowed in MySQL is not allowed in other database products including SQL Server. You should use a solution that will return the proper result each time it is executed.
So the query will be:
For one, when you apply the GROUP BY to only one column, there is no guarantee that the values in the other columns will be consistently correct. Unfortunately, MySQL allows this type of SELECT/GROUPing to happen other products don't. Two, the syntax of using an ORDER BY in a subquery while allowed in MySQL is not allowed in other database products including SQL Server. You should use a solution that will return the proper result each time it is executed.
So the query will be:
select CP.`id`, CP.`alias`, TK.`minPrice`
from catalog_products CP
left join `product_kits` PK on PK.`product_id` = CP.`id`
left join (
SELECT MIN(`new_price`) AS "minPrice", `id` FROM product_kits GROUP BY `id`
) AS TK on TK.`id` = PK.`id`
where CP.`category_id` IN ('62')
order by PK.`new_price` ASC
group by CP.`id`
The thing is that group by does not recognize order by in MySQL.
Actually, what I was doing is really bad practice.
In this case you should use distinct and by catalog_products.*
In my opinion, group by is really useful when you need group result of agregated functions.
Otherwise you should not use it to get unique values.
SELECT area_id,
area_name,
(select count(*) from applications
where claims_status=1 and
center_name=c.area_id) as cont
FROM apparea c where cont<>0
I am trying to get fields and relevant count from anothere table, but the above query is not working. The query is involved two different tables(apparea, applications). The above query has error and I am looking for the alternate way to achieve this.
The alias for your column cont is not available in the WHERE clause. You will want to use something similar to this:
SELECT area_id,
area_name,
cont
FROM
(
SELECT area_id,
area_name,
(select count(*)
from applications
where claims_status=1
and center_name=c.area_id) as cont
FROM apparea c
) c
where cont<>0
This can also be written using a LEFT JOIN:
select c.area_id,
c.area_name,
a.cont
from apparea c
left join
(
select count(*) cont,
center_name
from applications
where claims_status=1
group by center_name
) a
on c.area_id = a.center_name
Try this query
SELECT
c.area_id,
c.area_name,
cnt
FROM
apparea c,
(select
center_name,
count(*) AS cnt
from
applications
where
claims_status=1
GROUP BY
center_name
HAVING
count(*) > 0) cont
where
c.area_id = cont.center_name;
Got the count for each center_name and then joined table to get count for each area
Use HAVING rather than where.
As it is problem with aliases.
It is not permissible to refer to a column alias in a WHERE clause, because the column
value might not yet be determined when the WHERE clause is executed.
See Section C.5.5.4, “Problems with Column Aliases”.
http://dev.mysql.com/doc/refman/5.0/en/problems-with-alias.html
From: http://dev.mysql.com/doc/refman/5.0/en/select.html