MS Access 2010 SQL Top N query by group performance issue (continued2) - sql

I have rephrased a previous question MS Access 2010 SQL Top N query by group performance issue (continued) as I believe the context was not clearly described before. The anwwer to my previous question did not provide the top n by group result. The rephrased question is more generic. I have now all data in one table.
Here is my situation: I have a table (Analysis) that contains products (Partnumber) of various categories (Category). Every product has a price (Value). The objective of the query is to show the 10 products with the highest price of each category The table contains 15000 records and will continue to grow.
This is the query:
SELECT
a.Location,
a.Category,
a.Partnumber
a.Value
FROM Analysis a
WHERE a.Partnumber IN (
SELECT TOP 10 aa.Partnumber
FROM Analysis aa
WHERE aa.Category = a.Category
ORDER BY aa.Value DESC
)
ORDER BY
a.Category;
Here is my question: My current query works with 1000 records in the table (respond time 3 seconds). With 15000 records the query runs endlessly long. How can I rebuild the query to significantly improve performance?
The answer to my previous question was to not use the in-list operation. But this eliminated function to give the top n records by group. The query gave the top n of all records.

For sample data in a table called [Analysis]
ID Location Category Partnumber Value
-- --------- -------- ---------- -----
1 here cat1 part001 1
2 there cat1 part002 2
3 wherever cat1 part003 3
4 someplace cat2 part004 4
5 nowhere cat2 part005 5
6 unknown cat2 part006 6
the "ranking query"
SELECT
a1.ID,
a1.Location,
a1.Category,
a1.Partnumber,
a1.Value,
COUNT(*) AS CategoryRank
FROM
Analysis a1
INNER JOIN
Analysis a2
ON a1.Category = a2.Category
AND a1.Value <= a2.Value
GROUP BY
a1.ID,
a1.Location,
a1.Category,
a1.Partnumber,
a1.Value
returns
ID Location Category Partnumber Value CategoryRank
-- --------- -------- ---------- ----- ------------
1 here cat1 part001 1 3
2 there cat1 part002 2 2
3 wherever cat1 part003 3 1
4 someplace cat2 part004 4 3
5 nowhere cat2 part005 5 2
6 unknown cat2 part006 6 1
so if you only want the top 2 items in each category just wrap the above query in a SELECT ... WHERE
SELECT *
FROM
(
SELECT
a1.ID,
a1.Location,
a1.Category,
a1.Partnumber,
a1.Value,
COUNT(*) AS CategoryRank
FROM
Analysis a1
INNER JOIN
Analysis a2
ON a1.Category = a2.Category
AND a1.Value <= a2.Value
GROUP BY
a1.ID,
a1.Location,
a1.Category,
a1.Partnumber,
a1.Value
) AS RankingQuery
WHERE CategoryRank <= 2
ORDER BY Category, CategoryRank
to give you
ID Location Category Partnumber Value CategoryRank
-- -------- -------- ---------- ----- ------------
3 wherever cat1 part003 3 1
2 there cat1 part002 2 2
6 unknown cat2 part006 6 1
5 nowhere cat2 part005 5 2
Note: Ensure that the [Category] and [Value] fields are indexed for best performance.

Related

SQL method to return if all in join match conditions

I've been browsing this site for quite a while and this is my first post so I hope I've learned the correct way how to inquire... I've been stuck on this problem for quite a while even with all the available information I'm still lost.
I have 3 tables.
Sets, UserBudget, ItemCosts
table name column names
----------- ------------------------
Sets SetID
UserBudget UserID | ItemID | ItemBudget
ItemCosts SetID | ItemID | ItemPrice
ItemCosts contains multiple items, that are connected to a 'Sets' table entry.
I need a way to return the SetID's from the first table only if the UserBudget has all the same items as ItemCosts and all the ItemPrice is equal or lower to ItemBudget.
For example:
Sets ItemCosts UserBudget
----------- ---------- ----------
SetID 1 SetID 1 UserID 1
ItemID 1 2 ItemID 1 2
ItemPrice 10 12 ItemBudget 11 11
This is not supposed to return anything since the second item's price is higher than the user's budget for it.
Sets ItemCosts UserBudget
----------- ---------- ----------
SetID 1 SetID 1 UserID 1
ItemID 1 2 ItemID 1 2
ItemPrice 10 12 ItemBudget 11 12
This is supposed to return Set 1.
Any of my attempts just end up returning the set if one of the items matches the budget.
Here's my current SQL (it's a super simplified version in the end as all my complicated attempts took longer to execute but returned the same result):
SELECT *
FROM UserBudget a
INNER JOIN ItemCosts b on a.ItemID=b.ItemID
INNER JOIN Sets c on b.SetID=c.SetID
WHERE a.UserID=1 AND a.ItemBudget>=b.ItemPrice
You can try next solution:
SELECT a.UserID, GROUP_CONCAT(DISTINCT b.SetID) SetIDs
FROM UserBudget a
LEFT JOIN ItemCosts b on a.ItemID=b.ItemID AND a.ItemBudget>=b.ItemPrice
LEFT JOIN Sets c on b.SetID=c.SetID
WHERE a.UserID=1
GROUP BY a.UserID
HAVING COUNT(a.UserID) = COUNT(b.SetID)
;
MariaDB fiddle

Left Join Display All Data From Table1 and Table2

I am trying to do a left join so that I get all of my rows from Table 1 even if there is no value corresponding to it in the second table.
My structures are:
Location Table:
ID LocName
1 Trk1
2 Trk2
3 Trk3
4 Unk
Quantity Table:
ID PartID Quantity LocationID
1 1 2 1
2 3 12 2
3 2 6 1
4 6 8 3
5 6 5 1
I am trying to join but also make a query on a specific PartID. My query is:
SELECT
INV_LOCATIONS.ID AS LocationID,
INV_LOCATIONS.NAME AS LocationName,
INV_QUANTITY.QUANTITY AS Quantity
FROM INV_LOCATIONS
LEFT JOIN INV_QUANTITY ON INV_LOCATIONS.ID = INV_QUANTITY.LOCATION_ID
WHERE INV_QUANTITY.PART_ID = 1;
My output right now would be:
ID LocName Quantity
1 Trk1 5
3 Trk3 8
The Desired output is:
ID LocName Quantity
1 Trk1 5
2 Trk2 NULL/0
3 Trk3 8
4 Unk NULL/0
I assume it is because I have the WHERE INV_QUANTITY.PART_ID = 1 and that is forcing it to be in the quantity table. I need to be able to verify it is on the right part but how do I also include it if it doesn't exist. I know I have done something very similar before but I cannot remember which project and so I cannot find the code anywhere.
You need to move the filtering logic to the ON clause:
SELECT il.ID AS LocationID, il.NAME AS LocationName,
iq.QUANTITY AS Quantity
FROM INV_LOCATIONS il LEFT JOIN
INV_QUANTITY iq
ON il.ID = iq.LOCATION_ID AND iq.PART_ID = 1;

Find whether id matches and substitute using Case Hive query

I have a table called "Scan" customer transactions where an individual_id appears once for every different transaction and contains column like scan_id.
I have another table called ids which contains random individual_ids sampled from Scan Table
I would like to join ids with scan and get a single record of ids and scan_id if it matches certain values.
Suppose data is like below
Scan table
Ids scan_id
---- ------
1 100
1 111
1 1000
2 100
2 111
3 124
4 1000
4 111
Ids table
id
1
2
3
4
5
I want below output i.e if scan_id matches either 100 or 1000
Id MT
------ ------
1 1
2 1
3 0
4 1
I executed below query and got error
select MT, d.individual_id
from
(
select
CASE
when scan_id in (90069421,53971306,90068594,136739913,195308160) then 1
ELSE 0
END as MT
from scan cs join ids r
on cs.individual_id = r.individual_id
where
base_div_nbr =1
and
country_code ='US'
and
retail_channel_code=1
and visit_date between '2019-01-01' and '2019-12-31'
) as d
group by individual_id;
I would appreciate any suggestions or help with regard to this Hive query. If there is an efficient way of getting this job done. Let me know.
Use a group by:
select s.individual_id,
max(case when s.scan_id in (100, 1000) then 1 else 0 end) as mt
from scan s
group by s.individual_id;
The ids table doesn't seem to be needed for this query.

How to Order SQL Query using two columns?

accounts table
----------------
id name
10 ABC Company
11 XYZ Company
12 LMN Company
13 EFG Company
14 JKL Company
.. ...........
.. ...........
accounts_opportunities table
-----------------------
id opportunity_id account_id deleted
1 1 11 0
2 2 11 0
3 3 12 0
4 4 12 0
5 5 13 0
6 6 14 0
. . .. .
. . .. .
opportunities table
-----------------
id name amount
1 Opp 1 100
2 Opp 2 50
3 Opp 3 500
4 Opp 4 600
5 Opp 5 200
6 Opp 6 1000
I am trying to select top 20 accounts from above tables. And I written following query for
that,
SELECT TOP 20 COUNT(*) as number_of_opportunities,
(
SELECT accounts.name
FROM accounts
WHERE accounts.id=accounts_opportunities.account_id
) as account_name
FROM accounts_opportunities
JOIN opportunities ON opportunities.id = accounts_opportunities.opportunity_id
WHERE accounts_opportunities.deleted != '1'
GROUP BY accounts_opportunities.account_id
ORDER BY number_of_opportunities DESC
And above query gives following output;
Account name Number of Opportunities
------------ -----------------------
ABC Company 3
XYZ Company 2
LMN Company 2
EFG Company 1
JKL Company 1
XYZ and LMN have same numbers opportunities, but if I calculate total amount of XYZ opportunities are less than LMN total amount. Also JKL has higher amount than EFG.
My question is how to order this query from number_of_opportunities and then total opportunity amount. can someone please give me a guide..
Expected Output
Account name Number of Opportunities
------------ -----------------------
ABC Company 3
LMN Company 2
EFG Company 1
JKL Company 1
XYZ Company 2
Try this:
SELECT TOP 20 a.name, COUNT(*) AS [Number of Opportunities]
FROM accounts AS a
INNER JOIN accounts_opportunities AS ao ON a.id = ao.account_id
INNER JOIN opportunities AS o ON o.id = ao.opportunity_id
WHERE ao.deleted != 1
GROUP BY a.id, a.name
ORDER BY COUNT(*) DESC, SUM(amount) DESC
So, you can add SUM(amount) in the ORDER BY clause with DESC. This will place accounts with higher amount sums on top of other accounts, in case of a tie in COUNT(*).

Using with CUBE on the same column twice to output perculated results

Hypothetically, I have a table that consists of int values only one column with values like 1,2,3 etc., called Number.
When I try:
SELECT Number,Number FROM Table Group By Number WITH CUBE
It returns:
Number Number
------ ------
1 1
2 2
3 3
I was expecting it to return something more like this:
Number Number
------ ------
1 1
1 2
1 3
2 1
2 2
2 3
and so forth... (with every combination)
How would this be possible, WITH CUBE doesn't seem to be cutting it here.
It seems you want the cartesian product:
SELECT a.Number, b.Number
FROM [Table] a, [Table] b
Or, another way to write:
SELECT a.Number, b.Number
FROM [Table] a CROSS JOIN [Table] b