dependent SQL subquery returns too few rows - sql

I have a list of sales for hundreds of brands by shop and I want to get the TOP 100 brands per shop by sales.
For some unknown reason, it returns only 99 brands per shop. (In the source table there are more than 900 brands per each shop.)
That's my query:
SELECT TOP (100) PERCENT SHOP, BRAND, SALES
FROM dbo.[DATA] AS D
WHERE (BRAND IN
(SELECT TOP (100) WITH TIES BRAND
FROM DATA
WHERE (SHOP= D.SHOP)
ORDER BY SALES DESC
)
)
ORDER BY BRAND, SALES DESC
What went wrong?

I think what you are trying to do can be achieved by the following query
SELECT Q.SHOP
, Q.BRAND
, Q.SALES
FROM (
SELECT SHOP
, BRAND
, SALES
, ROW_NUMBER() OVER (PARTITION BY SHOP ORDER BY SALES DESC) rn
FROM dbo.[DATA]
) Q
WHERE Q.rn <= 100
ORDER BY Q.BRAND, Q.SALES DESC

Related

How to display duplicates of an aggregated attribute in Oracle SQL?

I have a query, where I am finding the maximum weight of different products in a table. Each product has a brand name and a Sku, and each brand name can have multiple sku's.
That query is:
select brand_name
, sku
, max(weight)
from bc.PRODUCTS
group by BRAND_NAME, SKU
order by 1
I want to display only those brand_names who have return multiple rows in the above query.
My working query is:
select max(weight)
, sku
, brand_name
from bc.PRODUCTS
group by BRAND_NAME, sku
having count(brand_name) > 1
order by 3
;
Yet that does not return any results.
I am very much a beginner, so any help would be appreciated.
;
We can subquery your current query and include the count across each brand:
WITH cte AS (
SELECT BRAND_NAME, SKU, MAX(weight) AS MAX_WEIGHT,
SUM(COUNT(*)) OVER (PARTITION BY bra BRAND_NAME) cnt
FROM bc.PRODUCTS
GROUP BY BRAND_NAME, SKU
)
SELECT BRAND_NAME, SKU, MAX_WEIGHT
FROM cte
WHERE cnt > 1;

How do I create a dynamic order by on a multiple UNION query?

I had a college exercise where I had to demonstrate a handy use of Union. I am using a sample TSQLV4 database.
I came up with the following code:
FROM
(SELECT TOP 5
cust.companyname as [Customer],
SUM(ord.total) AS [Total Spent],
COUNT(ord.orderid) AS [Total Orders],
'Top Spender' AS [Description]
FROM sales.Orders ord
JOIN sales.customers cust ON cust.custid = ord.custid
GROUP BY [companyname]
ORDER BY 2 desc) base
UNION
SELECT [Customer],[Total Spent],[Total Orders],[Description]
FROM
(SELECT TOP 5
cust.companyname as [Customer],
SUM(ord.total) AS [Total Spent],
COUNT(ord.orderid) AS [Total Orders],
'Lowest Spender' AS [Description]
FROM sales.Orders ord
JOIN sales.customers cust ON cust.custid = ord.custid
GROUP BY [companyname]
ORDER BY 2 ASC) base
UNION
SELECT [Customer],[Total Spent],[Total Orders],[Description]
FROM
(SELECT TOP 5
cust.companyname as [Customer],
SUM(ord.total) AS [Total Spent],
COUNT(ord.orderid) AS [Total Orders],
'Most Orders' AS [Description]
FROM sales.Orders ord
JOIN sales.customers cust ON cust.custid = ord.custid
GROUP BY [companyname]
ORDER BY 3 desc) base
UNION
SELECT [Customer],[Total Spent],[Total Orders],[Description]
FROM
(SELECT TOP 5
cust.companyname as [Customer],
SUM(ord.total) AS [Total Spent],
COUNT(ord.orderid) AS [Total Orders],
'Least Orders' AS [Description]
FROM sales.Orders ord
JOIN sales.customers cust ON cust.custid = ord.custid
GROUP BY [companyname]
ORDER BY 3 asc) base
order by 4,2
It returns:
Customer
Total Spent
Total Orders
Description
Customer VMLOG
100.8000
1
Least Orders
Customer UISOJ
357.0000
2
Least Orders
Customer GCJSG
649.0000
3
Least Orders
Customer FVXPQ
1488.7000
2
Least Orders
Customer EYHKM
1571.2000
3
Least Orders
Customer VMLOG
100.8000
1
Lowest Spender
Customer UISOJ
357.0000
2
Lowest Spender
Customer IAIJK
522.5000
3
Lowest Spender
Customer GCJSG
649.0000
3
Lowest Spender
Customer MDLWA
836.7000
5
Lowest Spender
Customer CYZTN
32555.5500
19
Most Orders
Customer FRXZL
57317.3900
19
Most Orders
Customer THHDP
113236.6800
30
Most Orders
Customer LCOUJ
115673.3900
31
Most Orders
Customer IRRVL
117483.3900
28
Most Orders
Customer NYUHS
52245.9000
18
Top Spender
Customer FRXZL
57317.3900
19
Top Spender
Customer THHDP
113236.6800
30
Top Spender
Customer LCOUJ
115673.3900
31
Top Spender
Customer IRRVL
117483.3900
28
Top Spender
It works fine for the purpose of the drill (and the homework got an A+), but I notice there is an issue with the sorting and can't seem to let it go, lol.
I chose to order the whole "UNIONED" query (please correct with the right nomenclature, if any) by the 4th column so the rows are stuck together by category, but then I had to chose between the second ORDER BY level to be the 2nd or 3rd columns (Total Spent, Total Orders). The problem is that if I sort by one or another, the second level sorting won't make sense for half of the categories (Least/Most orders sorted by total spent, or Lowest/Top Spenders sorted by total orders).
I have already created a table function that picks up a varchar (planning it to be the description column value) and returning the whole UNIONED query custom ordered, depending on IF statements that read the value for the #description parameter.
Now I cannot seem to create that last line that I thought would give me my intended behavior.
[...]
ORDER BY 3 asc) base
ORDER BY dbo.FtOrdenamiento(base.[Description])
Cannot find either column "dbo" or the user-defined function or aggregate "dbo.FtOrdenamiento", or the name is ambiguous.
Could anyone suggest a way to achieve this?
put all of your query in a cte and select your fields in the cto after that write your order by
for e.g:
WITH q
AS
(
SELECT p.ProductID,p.Name,p.ModifiedDate
FROM Production.Product p
UNION
SELECT ProductCategoryID,Name,ModifiedDate
FROM Production.ProductCategory
)
SELECT *
FROM q
ORDER BY q.Name,q.ModifiedDate
You can add a ROW_NUMBER in each of your subqueries and use this to order the final result:
e.g.
SELECT s.Client , s.InvoiceAmount,s.Description,s.RN
FROM (
SELECT TOP 5 f.Client , f.InvoiceAmount,'Small clients' as Description,ROW_NUMBER() OVER (ORDER BY f.InvoiceAmount) RN
FROM dbo.FactSales f
ORDER BY f.InvoiceAmount
) s
UNION ALL
SELECT l.Client , l.InvoiceAmount,Description,l.RN
FROM (
SELECT TOP 5 f.Client , f.InvoiceAmount,'Large clients' as Description,ROW_NUMBER() OVER (ORDER BY f.InvoiceAmount DESC) RN
FROM dbo.FactSales f
ORDER BY f.InvoiceAmount DESC
) l
ORDER BY Description,RN;

SQL Server different select statement on same table and different result

I have a products table that contains date-of-create, price, rate,....... and multiple columns I need one select statement that returns the 10 newest records, 10 lowest prices and the top rated products as separated result of each other something like
(select top 10 from products Order By Date ASC ) as newest_list
(select top 10 from products Order By price DESC ) as price_list
(select top 10 from products Order By Rate ASC ) as rateList_list
where ( newest_list price_list rateList_list) are different table result
Which better way to approach that result. Thanks.
You basically want union all . . . and a subquery:
select *
from ((select top 10 'newest' as which, p.*
from products p
Order By Date ASC
) union all
(select top 10 'priciest', p.*
from products p
Order By price DESC
) union all
(select top 10 'ratiest', p.*
from products p
Order By Rate ASC
)
) p
If you don't want duplicates, you can use union or window functions:
select p.*
from (select p.*,
row_number() over (order by date desc) as seqnum_d,
row_number() over (order by price desc) as seqnum_p,
row_number() over (order by rate asc) as seqnum_r
from p
) p
where seqnum_d <= 10 or seqnum_p <= 10 or seqnum_r <= 10;

SQL select x number of rows from table based on column value

I'm looking for a way to select top 3 rows from 4 vendors from a table of products, following this criteria:
Must select 4 vendors.
Must select top 3 products for each vendor ordered by product rating.
I tried doing something like:
select top 12 * product, vendor
from products
order by productrating
but obvisously that goesn't give me 3 products for each vendor.
The product table has:
productid (int), productname (nvarchar(500)), productrating (float),
vendor (id), price (float).
These are the relevant columns.
You can use the ANSI standard row_number() function to get 3 products for each vendor:
select p.*
from (select p.*,
row_number() over (partition by vendor order by rating desc) as seqnum
from products p
) p
where p.seqnum <= 3
If you want 4 vendors:
select top 12 p.*
from (select p.*,
row_number() over (partition by vendor order by rating desc) as seqnum
from products p
) p
where p.seqnum <= 3
order by vendor;
This will give you top 3 Products per vendor. You didn't specify how you're selecting the 4 vendors. That logic could easily be included using the WHERE clause or using a different ORDER BY depending on how you select the 4 vendors.
SELECT TOP 12 vnd.Vendor, apl.ProductName
FROM Vendors vnd
CROSS APPLY (
SELECT TOP 3 ProductID
FROM Products prd
WHERE vnd.ProductID = prd.ProductID
ORDER BY prd.ProductRating DESC
) apl
ORDER BY vnd.VendorName
If you have fixed list of vendors you would like to query you can use the following approach:
SELECT TOP 3
p.ProductID
FROM Products p
WHERE p.ProductID IN ( SELECT v.ProductID
FROM Vendors v
WHERE v.VendorID IN (Vendor1ID, Vendor2ID, Vendor3ID, Vendor4ID)
ORDER BY p.ProductRating DESC
You will need to look for a work-around if you have vendor names to select them filtering out by its name but keeping the join coindition trhough its IDs.

Oracle Complex Sort - Multiple Children

I have a table as follows:
BRAND_ID PRODUCT_ID PRODUCT_DESC PRODUCT_TYPE
100 1000 Tools A
100 1500 Tools A
200 2000 Burgers B
300 3000 Clothing C
300 4000 Makeup D
300 5000 Clothing C
So a Brand can have multiple products, all of the same type or mixed types. If a brands products are all of the same type I need them first in the result, sorted by product type, followed by brands that have different product types. I can do this programatically but I wanted to see if there is a way to do it in the query.
I don't have access to Oracle, but I believe something along these lines should work...
WITH
ranked_data
AS
(
SELECT
COUNT(DISTINCT product_type) OVER (PARTITION BY brand_id) AS brand_rank,
MIN(product_type) OVER (PARTITION BY brand_id) AS first_product_type,
*
FROM
yourTable
)
SELECT
*
FROM
ranked_data
ORDER BY
brand_rank,
first_product_type,
brand_id,
product_type,
product_description
An alternative is to JOIN on to a sub-query to calculate the two sorting fields.
SELECT
yourTable.*
FROM
yourTable
INNER JOIN
(
SELECT
brand_id,
COUNT(DISTINCT product_type) AS brand_rank,
MIN(product_type) AS first_product_type,
FROM
yourTable
GROUP BY
brand_id
)
AS brand_summary
ON yourTable.brand_id = brand_summary.brand_id
ORDER BY
brand_summary.brand_rank,
brand_summary.first_product_type,
yourTable.brand_id,
yourTable.product_type,
yourTable.product_description
How about selecting from a sub-select that figures out number of distinct brands and then sorting by the count?
select t.BRAND_ID,
t.PRODUCT_ID,
t.PRODUCT_DESC,
t.PRODUCT_TYPE
from (select t2.BRAND_ID,
t2.PRODUCT_ID,
count(distinct t2.PRODUCT_TYPE) cnt
from YOURTABLE t2
group by t2.BRAND_ID, t2.PRODUCT_ID) data
join YOURTABLE t on t.BRAND_ID = data.BRAND_ID and t.PRODUCT_ID = data.PRODUCT_ID
order by data.cnt, BRAND_ID, PRODUCT_ID, PRODUCT_TYPE