MySql: make this query faster... is there a way? - mysql

There are 4 tables:
Books : id, name, author, ecc...
Category : id, name
Library : id, name, street, city, ecc..
bookcorr : book_id, category_id, library_id
Ids are all keys.
The query must show the categories with the numbers of books in a defined Library. for ex:
Library X:
Romantic (50)
Yellow (40)
Science (30)
This is my query:
SELECT category.id
, category.name
, count(*) AS tot
FROM bookcorr
JOIN category
ON category.id = bookcorr.category_id
WHERE bookcorr.library_id = 'x'
GROUP BY bookcorr.category_id
ORDER BY tot DESC
and it's still slow, is there a way to get results faster ?

What indices do you have on these tables? The query suggests that bookcorr should have an index on (category_id, library_id).
Your query doesn't make use of Books or Library...

Change the query so that it would group on the leading table's column to avoid Using temporary:
SELECT category.id, category.name, COUNT(*) AS tot
FROM category
JOIN bookcorr
ON bookcorr.category_id = category.id
WHERE bookcorr.library_id = 'x'
GROUP BY
category.id
ORDER BY
tot DESC
and make sure that you have an index on bookcorr (library_id, category_id)

Related

SQL query to find the most number of vendors per a given location

Can you help me to find the proper MySQL query to get the most numbers of vendors per a given location and list them all by name and name of their shop:
1 - The query must find out which location has the highest number of vendors then list them by name with the name of the shop they work in.
I have the following tables:
CITIES
(
ID "unique",
NAME
)
SHOPS
(
ID "unique",
NAME,
CITY_ID ( foreign key of CITIES TABLE ID)
)
VENDORS
(
ID "unique",
NAME,
SHOP_ID ( foreign key of SHOPS TABLE ID)
)
Example with dummy data
CITIES : NY, SF
SHOPS: Boom - NY, Flash - NY, Sofast - SF
Vendors:
Mark : Boom,
John : Boom,
Carlos : Sofast,
Alex : Sofast,
David : Flash,
James: Flash
The NY has the highest number of vendors so it should list
Mark : Boom, John : Boom, David : Flash, James: Flash
Check if this works -
Select vendors.name, shops.name
from
cities inner join shops on cities.id= shops.city_id
inner join vendors on shops.id = vendors.shop_id
where cities.id = (select id from (select cities.id, count(1) from
cities inner join shops on cities.id= shops.city_id
inner join vendors on shops.id = vendors.shop_id
group by cities.id order by 2 desc) limit 1)
If you are running MySQL 8.0, you could approach this with window functions:
select *
from (
select x.*, rank() over(order by cnt) rn
from (
select v.*, count(*) over(partition by c.id) cnt
from cities c
inner join shops s on s.city_id = c.id
inner join vendors v on v.shop_id = s.id
) t
) t
where rn = 1
The most inner query joins the three tables, and counts how many vendors each city has. The next level ranks records by descending count. Finally, the last level filters on top-ranked rows.

SQL: Group by in subquery

I'm trying to find the output of all books that have more than one genre using a group by statement and subquery. However, it keeps returning Subquery returns more than 1 row. This is what I have so far:
SELECT title
FROM book
WHERE 1 < (SELECT COUNT(genre) FROM genres GROUP BY book_id);
Here's an example:
SELECT b.title
FROM ( SELECT g.book_id
FROM genres g
GROUP
BY g.book_id
HAVING COUNT(1) > 1
) m
JOIN book b
ON b.id = m.book_id
The inline view m is meant to return us values of book_id that appear more than one time in the genres table. Depending on uniqueness constraints, we might want to count distinct values of genre
HAVING COUNT(DISTINCT g.genre) > 1
if we want to find books with exactly three related genre:
HAVING COUNT(DISTINCT g.genre) = 3
Once we have a list of book_id values, we can join to the book table. (The query assumes that book_id in genres is a foreign key reference to the id column in book table.)
You seem to what a correlated subquery:
SELECT b.title
FROM book b
WHERE 1 < (SELECT COUNT(*) FROM genres g WHERE g.book_id = b.book_id);
SELECT distinct a.title
FROM book a, (select bookid,count(distinct genre)genres from genres group by bookid)b
WHERE a.book_id=b.bookid and b.genres>1
hope it helps!

redshift query to get desired output

In redshift for a provided dataset for a restaurant
Every Dish_id is being assigned to a category
So based on the distinct orders which are being palced
I need to find out for a provided primary dish what all other items went along with it
primary item (every distinct item of that restaurant will act as a primary dish once)
Currently i am able to do it for a single dish_id and getting its contribution
Select category_name,count(category_name) from (
Select order_id,dish_id,dish_name,category_id,category_name from abc
where order_id in (Select distinct order_id from abc where dish_name='Paneer_pizza' and restaurant_id=1)
group by order_id,dish_id,dish_name,category_id,category_name
order by category_name
)
group by category_name
Question's
How can i print Panner_pizza in the outer query along with category_name and count?
How can i pass all the dish_name present in that restaurant in inner query
and get the contribution for all the dishes along with count for all the categories?
Hard to tell exactly what you want...you might need two queries. But I think an inner query like this might help. This will filter the results to just the primary dish and restaurant that you are interested in and add the primary dish to each record in the order.
select a.order_id, primary_dish, dish_id, dish_name, category_id, category_name
from abc a
inner join (
Select distinct order_id, dish_name as primary_dish
from abc
where dish_name='Paneer_pizza' and restaurant_id=1
) b on b.order_id = a.order_id
where primary_dish != dish_name --optionally exclude the primary dish record
So count of other dish categories for the primary dish would be:
with dishes as (
select a.order_id, primary_dish, dish_id, dish_name, category_id, category_name
from abc a
inner join (
Select distinct order_id, dish_name as primary_dish
from abc
where dish_name='Paneer_pizza' and restaurant_id=1
) b on b.order_id = a.order_id
where primary_dish != dish_name --optionally exclude the primary dish record
)
select primary_dish, category_name, count(*) from dishes
group by primary_dish, category_name
I think this might be what you actually want. This gives all the dishes in the restaurant treating each as a primary dish and then gives dishes ordered with the primary, their category and the total number of orders placed that included the secondary dish:
with dishes as (
select a.order_id, primary_dish, dish_id, dish_name, category_id, category_name
from abc a
inner join (
Select distinct order_id, dish_name as primary_dish
from abc
where restaurant_id=1
) b on b.order_id = a.order_id
where primary_dish != dish_name --optionally exclude the primary dish record
)
select primary_dish, dish_name, category_name, count(*) from dishes
group by primary_dish, dish_name, category_name

How to change query to nested select?

I have a project where I have to use nested select and operators like EXISTS, IN, ALL, ANY, to find the name of the owners who own more apartments.
I have the 2 tables owners and apartments
Owner
"ID" NUMBER(5,0),
"NAME" VARCHAR2(20),
"PHONE" NUMBER(10,0),
CONSTRAINT "PROPR" PRIMARY KEY ("ID")
USING INDEX ENABLE;
Apartment
"ID_AP" NUMBER(5,0),
"ADDRESS" VARCHAR2(35),
"SURFACE" NUMBER(10,0),
"ID" NUMBER(5,0),
CONSTRAINT "APART" PRIMARY KEY ("ID_AP")
USING INDEX ENABLE;
In order to find the owners who have more than one apartment, I wrote this
SELECT name, id, count(id)
from apartment join owner
using (id)
group by id, name
having count(id)>1
But how do I use nested Selects and one of the operators EXISTS, IN, ALL, ANY?
For example like this:
select * from owner where id IN (
SELECT id
from apartment
group by id
having count(id)>1)
I am not quite sure that the query you wrote is correct .
The correct query that you provided would count(id_ap) instead of count(id)
SELECT o.id, o.name, count(a.id_ap)
from apartment a
join owner o on o.id = a.id
group by o.id, o.name
having count(a.id_ap)>1
The same query using in clause, but without the number of apartments, you only know that it's 2 or more
select o.id, o.name
from owner o
where o.id in (select distinct(a.id) from apartment a
group by a.id having count(a.id_ap) > 1);
You are missing a foreign key constraint on apartment table referencing owner table as well.
WITH myView(name, id,appcount)
as
SELECT name, id, (select count(id) FROM apartment where id = ow.id)
FROM owner ow
SELECT * from myView
WHERE appcount > 2
Create a inline View using WITH and use to write a query. (select count(id) FROM apartment where id = ow.id) in select of outer query will calculate apartment count.

How can I update the value of one field to the most often used value of another field?

I have a table similar to the following:
ID PAYEE CATEGORY
001 Costco Grocery
002 See's Candy
003 Costco Repair
005 Costco Grocery
006 Costco
007 Costco
008 See's
Using MySQL withOUT the aid of a programming language, is there a query (nested or not) that would set the category of the three new rows to the most often used category for those payees?
For example, one of the Costco records (ID 003) has Repair as its category, whereas the other two Costco rows (ID 001 and ID 005) have Grocery as their category. Thus the desired result would be that the new Costco rows (ID 006 and ID 007) would be set to Grocery since that's the most often used category for that payee.
sure.. just change 'your_table' to the name of your table
UPDATE your_table
LEFT JOIN (SELECT payee, category
FROM
(SELECT payee, category FROM your_table WHERE category != '' AND category IS NOT NULL GROUP BY payee, category ORDER BY count(*) DESC) AS tbl2
GROUP BY payee
) AS tbl2 USING (payee)
SET your_table.category = tbl2.category;
this will change the costco that is categorized as repair to 'grocery' as well.. if you dont want this, add:
WHERE your_table.category IS NULL OR your_table.category = ''
to the very end of the query
This would work
UPDATE test t,
(SELECT category,
payee,
count(*)
FROM test ORDER BY count(*) desc LIMIT 1) t1
SET t.category = t1.category
WHERE t.payee = t1.payee
AND (t.category = ''
OR t.category IS NULL)
Sqlfiddle at http://www.sqlfiddle.com/#!2/ed5b0/1/0
I just couldn't figure out a way without repeating the creation of a derived table:
UPDATE t JOIN (
SELECT s1.payee, s1.category FROM (
SELECT payee, category, count(*) cat_count FROM t
WHERE category IS NOT NULL
GROUP BY payee, category
) s1
LEFT JOIN (
SELECT payee, category, count(*) cat_count FROM t
WHERE category IS NOT NULL
GROUP BY payee, category
) s2
ON s1.payee = s2.payee AND s1.cat_count < s2.cat_count
WHERE s2.cat_count IS NULL
) s
ON t.payee = s.payee
SET t.category = s.category
WHERE t.category IS NULL;
Fiddle here
Use mysql's multi-table update:
UPDATE mytable t
JOIN (SELECT payee, category
FROM (SELECT payee, category
FROM mytable
GROUP BY 1, 2
ORDER BY count(*) desc) x
GROUP BY 1) y
ON y.payee = t.payee
SET t.category = y.category
WHERE ifnull(t.category, '') = ''
There's a little bit of kung fu tucked away in there that makes it work: the outer group by returns the first row encountered for the group, which due to the ordering for the inner-most query will be the category with the highest count.