Maria DB SQL group by and sum query is slow - sql

My My SQl query is taking 6 sec to execute, I have added index on the where field, but its still same speed, then I remove the where clause and check the speed, the different is very small. So the problem is in SUM and Group By, I have read to optimize Group by adding index on Group BY fields but they are on different columns. Not an Expert in Mysql im a java developer.
SELECT
IF(
categories_1.NAME IS NULL,
categories.name,
categories_1.name
) AS PARENTCAT,
categories.NAME AS SUBCAT,
products.REFERENCE AS PRODREF,
products.NAME AS PRODNAME,
SUM(ticketlines.UNITS) AS UNITS,
SUM(ticketlines.PRICE * ticketlines.UNITS) AS SALES
FROM
taxes taxes
INNER JOIN ticketlines ticketlines ON (taxes.ID = ticketlines.TAXID)
INNER JOIN products products ON (products.ID = ticketlines.PRODUCT)
RIGHT OUTER JOIN categories categories ON (categories.ID = products.CATEGORY)
LEFT OUTER JOIN categories categories_1 ON (categories.PARENTID = categories_1.ID)
INNER JOIN tickets tickets ON (tickets.ID = ticketlines.TICKET)
INNER JOIN receipts receipts ON (tickets.ID = receipts.ID)
WHERE
(
receipts.DATENEW BETWEEN DATE('2021-12-01 00:00:00.000')
AND DATE('2022-02-18 00:21:00.000')
)
GROUP BY
categories.NAME,
products.REFERENCE
ORDER BY
PARENTCAT ASC,
SUBCAT ASC,
PRODNAME ASC
The Index in on receipts.DATENEW

Related

Using MS Access SQL - how would I select the top 5 food items by group (restaurant) based on the number of orders?

Relationship Diagram
What I want to be able to do is return a query that shows the top 5 items/products on the menu for each of the 3 restaurants in the dataset. I've attached an example of the relationship diagram for some more context. The columns I would like to see in the query are:
RestaurantName
ItemName
NumberofOrders (alias column)
This is what I have at the moment but it doesn't work as expected for the top 5.
SELECT RestaurantName, ItemName, COUNT(Orders.OrderNumber) AS NumberofOrders
FROM ((((Restaurants INNER JOIN
Orders ON Restaurants.RestID = Orders.RestID) INNER JOIN
OrderDetails ON Orders.OrderNumber=OrderDetails.OrderNumber) INNER JOIN
Products ON OrderDetails.ItemID = Products.ItemID) INNER JOIN
FoodType ON Products.ProdTypeID = FoodType.ProdType)
WHERE ItemName IN
(SELECT TOP 5 ItemName
FROM Products
WHERE ItemName IS NOT NULL)
GROUP BY RestaurantName, ItemName
ORDER BY COUNT(Orders.OrderNumber) DESC;
This just repeats the same 5 items across all the restaurants. Any point in the right direction would be awesome.
EDIT 1:
Based on a response I got yesterday, I have made some amendments to the code. This the query is returning the full list, as though ignoring the top 5 in the subquery. I can see all the items are sorted by Total Orders (I have also changed the formula for this) Any ideas what I am doing wrong here?
SELECT RestaurantName, ItemName, SUM(Quantity)*COUNT(Orders.OrderNumber) AS TotalOrders
FROM ((((Restaurants INNER JOIN
Orders ON Restaurants.RestID = Orders.RestID) INNER JOIN
OrderDetails ON Orders.OrderNumber=OrderDetails.OrderNumber) INNER JOIN
Products ON OrderDetails.ItemID = Products.ItemID) INNER JOIN
FoodType ON Products.ProdTypeID = FoodType.ProdType)
WHERE ItemName IN
(SELECT TOP 5 p2.ItemName
FROM Products AS p2
WHERE p2.ItemName = Products.ItemName
GROUP BY p2.ItemName
ORDER BY COUNT(*) DESC)
GROUP BY RestaurantName, ItemName
ORDER BY RestaurantName, SUM(Quantity) DESC;
Thanks
You want a correlated subquery:
WHERE ItemName IN (SELECT TOP 5 p2.ItemName
FROM Products as p2
WHERE p2.RestaurantName = products.RestaurantName
GROUP BY p2.ItemName
ORDER BY COUNT(*) DESC
)
It seems really odd to me that a table called products would have a column called RestaurantName. But you claim that your query works and it has the same reference.
Your filter in the outer WHERE is only using the ItemName field. For your purpose it should contain both fields.
Like so:
SELECT
RestaurantName,
ItemName,
COUNT(Orders.OrderNumber) AS NumberofOrders
FROM Restaurants
INNER JOIN Orders ON Restaurants.RestID = Orders.RestID
INNER JOIN OrderDetails ON Orders.OrderNumber=OrderDetails.OrderNumber
INNER JOIN Products ON OrderDetails.ItemID = Products.ItemID
INNER JOIN FoodType ON Products.ProdTypeID = FoodType.ProdType
WHERE (RestaurantName, ItemName) IN
(SELECT TOP 5 RestaurantName, ItemName
FROM Products
WHERE RestaurantName IS NOT NULL)
GROUP BY RestaurantName, ItemName
ORDER BY COUNT(Orders.OrderNumber) DESC;

SQL - Sum Up Results Of A Multiplication By Group

I have the following tables:
Orders (OID, Count, ProductID, TableNr)
Table (TableNr, Name, Number)
Products (ProductID, Prize)
Now I want to calculate how much was earned per table. I think to do this I have to Group By Orders.TableNr. But how can I multiply the Products.Prize with the Orders.Count and after that sum up this results within the group?
You are describing a join and aggregation:
select o.tableNr, sum(o.count * p.prize) totalEarned
from orders o
inner join products p on p.productId = o.productId
group by o.tableNr
If you want to display table information as well (say, the table name), then you can add another join:
select t.tableNr, t.name, sum(o.count * p.prize) totalEarned
from table t
inner join orders o on o.tableNr = t.tableNr
inner join products p on p.productId = o.productId
group by t.tableNr, t.name
Note that table is a SQL keyword, hence not a good choice for a table name.

SQL: How to group by with two tables?

I have the tables products and history and I need to group by name:
products = (id_product, name)
history = (id_history, id_product, amount)
I tried this SQL query but it isn't grouped by name:
SELECT
products.name,
sum(history.amount)
FROM history
INNER JOIN products ON history.id_product = products.id_product
GROUP BY
products.name,
history.amount,
history.id_history;
This is the result:
You should only be grouping by the attributes you need to be aggregated. In this case, you need only products.name.
SELECT
products.name,
sum(history.amount) AS [Amount]
FROM history
INNER JOIN products ON history.id_product = products.id_product
GROUP BY
products.name;
If you need to include products without history (assuming sum should be 0 instead of null in this case), then you can use an OUTER JOIN instead of INNER JOIN to include all products:
SELECT
products.name,
COALESCE(sum(history.amount), 0) AS [Amount]
FROM history
RIGHT OUTER JOIN products ON history.id_product = products.id_product
GROUP BY
products.name;
This is no answer, but too long for a comment.
For readability's sake the product table should be first. After all it is products that we select from, plus a history sum that we can access via [left] join history ... followed by an aggregation, or [left] join (<history aggregation query>), or a subselect in the select clause.
Another step to enhance readability is the use of alias names.
Join the table, then aggregate
select p.name, coalesce(sum(h.amount), 0) as total
from products p
left join history h on h.id_product = p.id_product
group by p.name
order by p.name;
Aggregate, then join
select p.name, coalesce(h.sum_amount, 0) as total
from products p
left join
(
select sum(h.amount) as sum_amount
from history
group by id_product
) h on h.id_product = p.id_product
order by p.name;
Get the sum in the select clause
select
name,
(select sum(amount) from history h where h.id_product = p.id_product) as total
from products p
order by p.name;
And as you were confused on how to use GROUP BY, here is an explanation: GROUP BY ___ means you want one result row per ___. In your original query you had GROUP BY products.name, history.amount, history.id_history saying you wanted one result row per name, amount, and id, while you actually wanted one row per name only, i.e. GROUP BY products.name.

Using sum with a nested select

I'm using SQL Server. This statement lists my products per menu:
SELECT menuname, productname
FROM [web].[dbo].[tblMenus]
FULL OUTER JOIN [web].[dbo].[tblProductsRelMenus]
ON [tblMenus].Id = [tblProductsRelMenus].MenuId
FULL OUTER JOIN [web].[dbo].[tblProducts]
ON [tblProductsRelMenus].ProductId = [tblProducts].ProductId
LEFT JOIN [web].[dbo].[tblOrderDetails]
ON ([tblProducts].Id = [tblOrderDetails].ProductId)
GROUP BY [tblProducts].ProductName
Some products don't have menus and vice versa. I use the following to establish what has been sold of each product.
SELECT [tblProducts].ProductName, SUM([tblOrderDetails].Ammount) as amount
FROM [web].[dbo].[tblProducts]
LEFT JOIN [web].[dbo].[tblOrderDetails]
ON ([tblProducts].ProductId = [tblOrderDetails].ProductId)
GROUP BY [tblProducts].ProductName
What I want to do is complement the top table with an amount column. That is, I want a table with the same number of rows as in the first table above but with an amount value if it exists, otherwise null.
I can't figure out how to do this. Any suggestions?
If I am not missing anything, the second query could be simplified, then incorporated into the first query like this:
SELECT
m.menuname,
p.productname,
t.amount
FROM [web].[dbo].[tblMenus] m
FULL JOIN [web].[dbo].[tblProductsRelMenus] pm ON m.Id = pm.MenuId
FULL JOIN [web].[dbo].[tblProducts] p ON pm.ProductId = p.ProductId
LEFT JOIN (
SELECT ProductId, SUM(Amount) as amount
FROM [web].[dbo].[tblOrderDetails]
GROUP BY ProductId
) t ON p.ProducId = t.ProductId

Combining multiple SQL Queries

I want to make a query to list cats that took longer than average cats to sell?
I have five tables:
Animal, Sale, AnimalOrderItem, AnimalOrder, and SaleAnimal
Animal table: AnimalID, Name, Category
(cat, dog, fish)
SaleAnimal table: SaleID, AnimalID,
SalePrice
Sale table: SaleID, date, employeeID,
CustomerID
AnimalOrderItem table: OrderID,
AnimalID, cost
AnimalOrder: OrderID, OrderDate,
ReceivingDate,
SupplierID, ShippingCost, EmployeeID
There is other tables I don’t think they have an effect on the query.
I thought of the following ... make a query to calculate days to sell for all ex.:
[SaleDate]-[ReceiveDate] AS DaysToSell
Have the INNER JOIN built:
Sale INNER JOIN ((AnimalOrder INNER JOIN (Animal INNER JOIN AnimalOrderItem
ON Animal.AnimalID = AnimalOrderItem.AnimalID) ON AnimalOrder.
OrderID = AnimalOrderItem.OrderID) INNER JOIN SaleAnimal ON Animal.
AnimalID = SaleAnimal.AnimalID) ON Sale.SaleID = SaleAnimal.SaleID
Create another query based on the above query
SELECT AnimalID, Name, Category, DaysToSell
WHERE Category="Cat" AND DaysToSell>
(SELECT Avg(DaysToSell)
FROM the earlier query
WHERE Category="Cat"
ORDER BY DaysToSell DESC;
After running the query it I got error saying
ORA-00921: unexpected end of SQL
command
Any suggestions! please
Queries can be combined with a subquery. For example,
select *
from (
select *
from mytable
) subquery
Applying this pattern to your problem seems fairly straightforward.
I don't see the closed bracket that matches with the select avg
Ok, I've come up with this:
SELECT AnimalID, Name, Category,
[SaleDate]-[ReceiveDate] AS DaysToSell
FROM Sale INNER JOIN ((AnimalOrder INNER JOIN (Animal INNER JOIN AnimalOrderItem ON Animal.AnimalID = AnimalOrderItem.AnimalID) ON AnimalOrder.OrderID = AnimalOrderItem.OrderID)
INNER JOIN SaleAnimal ON Animal.AnimalID = SaleAnimal.AnimalID) ON Sale.SaleID = SaleAnimal.SaleID
WHERE Category = "Cat"
AND ([SaleDate]-[ReceiveDate]) > (SELECT AVG([SaleDate]-[ReceiveDate])
FROM Sale INNER JOIN ((AnimalOrder INNER JOIN (Animal INNER JOIN AnimalOrderItem ON Animal.AnimalID = AnimalOrderItem.AnimalID) ON AnimalOrder.OrderID = AnimalOrderItem.OrderID)
INNER JOIN SaleAnimal ON Animal.AnimalID =SaleAnimal.AnimalID) ON Sale.SaleID = SaleAnimal.SaleID
WHERE Category = "Cat")
ORDER BY ([SaleDate]-[ReceiveDate]) DESC;