select if not a count - sql

I have a 3 tables Product, sku and option
Product
ID FlatID Name
314E5E5E-0A7E-4DC5-872C-16DA8DA36439 0706 Jacket
SKU
ID productID QTY
6CFE849A-4856-43E8-9837-3B1D1122A701 314E5E5E-0A7E-4DC5-872C-16DA8DA36439 1
F76C9B5B-CEB5-44FE-AD27-69C4A3C124AE 314E5E5E-0A7E-4DC5-872C-16DA8DA36439 1
BB413DE8-E310-4393-95E9-8262D6EE515A 314E5E5E-0A7E-4DC5-872C-16DA8DA36439 1
866CD899-F25F-4C4D-9E56-A4A95A7BDC5D 314E5E5E-0A7E-4DC5-872C-16DA8DA36439 1
Options
skuid OptionID name
6CFE849A-4856-43E8-9837-3B1D1122A701 1C823E53-EE98-4B22-8118-287BCA55C4D8 BK
6CFE849A-4856-43E8-9837-3B1D1122A701 6D7168A0-775A-4C5B-B8E8-8CE3AF8A8665 LG
F76C9B5B-CEB5-44FE-AD27-69C4A3C124AE 1C823E53-EE98-4B22-8118-287BCA55C4D8 BK
F76C9B5B-CEB5-44FE-AD27-69C4A3C124AE F6A0974C-303D-4C6A-8CE1-39071C7DF566 MD
BB413DE8-E310-4393-95E9-8262D6EE515A FB4D7BC1-6C71-4E00-8C85-0DE93DC1C531 SM
BB413DE8-E310-4393-95E9-8262D6EE515A 1C823E53-EE98-4B22-8118-287BCA55C4D8 BK
866CD899-F25F-4C4D-9E56-A4A95A7BDC5D F6E0FB61-047E-4356-B085-630B38CBC7F2 XL
How to select SKU which don't have a one of option - for example in this case SKU 866CD899-F25F-4C4D-9E56-A4A95A7BDC5D don't have a color option with name BK - but should.
Sometimes product(SKU) have a one or two or three options - and how find a SKU which don't have a some of option - may be need to get max count and compare but i dont know. ThanX
For example one product have a 2 sku one of them have a tree option - other one have a one option how to show that have not max option for SKU

I usually prefer left outer joins for this problem where you are getting all the rows in table A, in this case SKU, regardless of there is a match in B, or options. then use the where clause to filter out the matches so you are left with only skus without options.
SELECT SKU.*
FROM SKU
LEFT OUTER JOIN Options
ON Options.skuid = SKU.id
WHERE Options.skuid IS NULL

You don't want any details of option table you just want check whether skuid present in option table so..
SELECT *
FROM sku A
WHERE NOT EXISTS (SELECT 1
FROM options
WHERE options.skuid = A.skuid)

Related

COUNT with multiple LEFT joins [duplicate]

This question already has answers here:
Two SQL LEFT JOINS produce incorrect result
(3 answers)
Closed 12 months ago.
I am having some troubles with a count function. The problem is given by a left join that I am not sure I am doing correctly.
Variables are:
Customer_name (buyer)
Product_code (what the customer buys)
Store (where the customer buys)
The datasets are:
Customer_df (list of customers and product codes of their purchases)
Store1_df (list of product codes per week, for Store 1)
Store2_df (list of product codes per day, for Store 2)
Final output desired:
I would like to have a table with:
col1: Customer_name;
col2: Count of items purchased in store 1;
col3: Count of items purchased in store 2;
Filters: date range
My query looks like this:
SELECT
DISTINCT
C_customer_name,
C.product_code,
COUNT(S1.product_code) AS s1_sales,
COUNT(S2.product_code) AS s2_sales,
FROM customer_df C
LEFT JOIN store1_df S1 USING(product_code)
LEFT JOIN store2_df S2 USING(product_code)
GROUP BY
customer_name, product_code
HAVING
S1_sales > 0
OR S2_sales > 0
The output I expect is something like this:
Customer_name
Product_code
Store1_weekly_sales
Store2_weekly_sales
Luigi
120012
4
8
James
100022
6
10
But instead, I get:
Customer_name
Product_code
Store1_weekly_sales
Store2_weekly_sales
Luigi
120012
290
60
James
100022
290
60
It works when instead of COUNT(product_code) I do COUNT(DSITINCT product_code) but I would like to avoid that because I would like to be able to aggregate on different timespans (e.g. if I do count distinct and take into account more than 1 week of data I will not get the right numbers)
My hypothesis are:
I am joining the tables in the wrong way
There is a problem when joining two datasets with different time aggregations
What am I doing wrong?
The reason as Philipxy indicated is common. You are getting a Cartesian result from your data thus bloating your numbers. To simplify, lets consider just a single customer purchasing one item from two stores. The first store has 3 purchases, the second store has 5 purchases. Your total count is 3 * 5. This is because for each entry in the first is also joined by the same customer id in the second. So 1st purchase is joined to second store 1-5, then second purchase joined to second store 1-5 and you can see the bloat. So, by having each store pre-query the aggregates per customer will have AT MOST, one record per customer per store (and per product as per your desired outcome).
select
c.customer_name,
AllCustProducts.Product_Code,
coalesce( PQStore1.SalesEntries, 0 ) Store1SalesEntries,
coalesce( PQStore2.SalesEntries, 0 ) Store2SalesEntries
from
customer_df c
-- now, we need all possible UNIQUE instances of
-- a given customer and product to prevent duplicates
-- for subsequent queries of sales per customer and store
JOIN
( select distinct customerid, product_code
from store1_df
union
select distinct customerid, product_code
from store2_df ) AllCustProducts
on c.customerid = AllCustProducts.customerid
-- NOW, we can join to a pre-query of sales at store 1
-- by customer id and product code. You may also want to
-- get sum( SalesDollars ) if available, just add respectively
-- to each sub-query below.
LEFT JOIN
( select
s1.customerid,
s1.product_code,
count(*) as SalesEntries
from
store1_df s1
group by
s1.customerid,
s1.product_code ) PQStore1
on AllCustProducts.customerid = PQStore1.customerid
AND AllCustProducts.product_code = PQStore1.product_code
-- now, same pre-aggregation to store 2
LEFT JOIN
( select
s2.customerid,
s2.product_code,
count(*) as SalesEntries
from
store2_df s2
group by
s2.customerid,
s2.product_code ) PQStore2
on AllCustProducts.customerid = PQStore2.customerid
AND AllCustProducts.product_code = PQStore2.product_code
No need for a group by or having since all entries in their respective pre-aggregates will result in a maximum of 1 record per unique combination. Now, as for your needs to filter by date ranges. I would just add a WHERE clause within each of the AllCustProducts, PQStore1, and PQStore2.

Filter with SQL Server by Group ID

I have two tables and I need to filter the data by filter id depends on the relation to to filter group id.
For example I have this two tables:
Table 1:
ItemID
FilterID
3
122
3
123
3
4
17
123
Table 2:
FilterID
FilterGroupID
122
5
123
5
4
1
If I search by filter id = 123 than all item id with this filter need to be returned.
If I search two or more different filter id that have different group id I need to get only the item ids that have all filter id and group id.
Desired output:
first input: 123 -> return item id =3 and item id = 17
second input: 123,4 -> return item id = 3 because filter id 123 belong to group id 5 and filter id 4 belong to group id 1 and item id 3 is the only one that has this two filters.
third input: 122,123 -> return item id =3 and item id = 17 because both filter id belong to same group.
I am getting a little lost with this query and I will be glad to get some help.
I’ll try to simplify it: Let’s say we have group filter of size and group filter of color. If I filter by size S or M than I need to get all items with this sizes. If I want to add color like blue than the answer will cut the result by: item with size S or M and Color blue. So filter from different group may cut some results
It seems that you want to get every ItemID which has at least one matching filter from each FilterGroupID within your filter input. So within each group you have or logic, and between groups you have and logic
If you store your input in a table variable or Table-Valued parameter, then you can just use normal relational division techniques.
This then becomes a question of Relational Division With Remainder, with multiple divisors.
There are many ways to slice this cake. Here is one option
Join the filter input to the groups, to get each filter's group ID
Use a combination of DENSE_RANK and MAX to get the total distinct groups (you can't use COUNT(DISTINCT in a window function so we need to hack it)
You can change this step to use a subquery instead of window functions. It may be faster or slower
Join the main table, and filter out any ItemIDs which do not have their total distinct groups the same as the main total
SELECT
t1.ItemID
FROM (
SELECT *,
TotalGroups = MAX(dr) OVER ()
FROM (
SELECT
fi.FilterID,
t2.FilterGroupID,
dr = DENSE_RANK() OVER (ORDER BY t2.FilterGroupID)
FROM #Filters fi
JOIN Table2 t2 ON t2.FilterID = fI.FilterID
) fi
) fi
JOIN Table1 t1 ON t1.FilterID = fi.FilterID
GROUP BY
t1.ItemID
HAVING COUNT(DISTINCT FilterGroupID) = MAX(fi.TotalGroups);
db<>fiddle

SUM each row with corresponding row of another column

I'm trying to update the values of a select number of rows from one column to each be incremented by a certain value taken from another column from another table but I keep hitting this wall : 'single-row subquery returns more than one row'.
Essentially what this is all about is deleting a hypothetical shopping cart (each row in the shopping cart corresponds to a certain item and a desired quantity of that item) but before doing that I must first make sure that all the item quantities from the cart are returned to the product relation which keeps track of all available items and the quantity of those items currently on supply.
UPDATE Product SET supply = (...) - subquery returning a single column list of all quantites to be added back to the respective product supplies, ordered by the product_id - even though it's not included in the select statement
WHERE product_id IN (...) - same query as the last one but returns the product ids of all the corresponding shopping cart items rather than their quantities, again ordered by product_id
All looks fine but ORACLE's not having it. Perhaps this can be looped somehow? If not I'll have to resort to doing it in the app layer (PHP)
Product relation:
product_id
Supply
...
1
10
...
2
20
...
3
10
...
Basket relation:
product_id
quantity
...
1
3
...
2
4
...
Upon the deletion of the cart I would like the two quantities from the cart table to be added back to their corresponding rows in the supply column in the product relation
This:
product_id
Supply
...
1
10
...
2
20
...
3
10
...
Turns into this:
product_id
Supply
...
1
13
...
2
24
...
3
10
...
You can use corelated subquery as follows:
Update product p
Set p.supply = p.supply + coalesce(select quantity from basket b
Where b.product_id = p.product_id), 0)
It is considered good to use exists to reduce the redo logs as follows:
Update product p
Set p.supply = p.supply + (select quantity from basket b
Where b.product_id = p.product_id)
WHERE EXISTS
(Select 1 from basket b
Where b.product_id = p.product_id)
Please note that we have removed the coalesce in this query as it is not required if we use exists.

How to exclude products that have variations missing from another table?

This is a SQL query that I need to make, in which I have a parent Product. Under each product, multiple variations(again products) can be nested. Some of the variations may not be posted to Channel and I need to exlude all parents that have at least 1 child not included in ChannelReport.
WHERE 1=1
AND bvc_Product.ClientID=100
AND bvc_Product.Status=1
AND VariationProduct.ProductID is not null
AND VariationProduct.ChildProductID IN (SELECT productid FROM
ChannelReport wpal WHERE wpal.productid =
VariationProduct.ChildProductID)
Sample data would be:
T-Shirt1 - Product ID
S, M, L - 3 different variations. Each technically a productID as well.
T-Shirt2 - Product ID
S, M, L - 3 different variations.
T-Shirt1-M is not on ChannelReport as a productID. All T-Shirt2 variations are in ChannelReport. In this case, the result should be only:
T-Shirt2
Even without seeing your table, I can tell you this. Any time you have a requirement like:
all x that have at least 1 y
You need to use EXISTS(). Psuedocode for your requirement:
WHERE NOT EXISTS(SELECT child FROM Table WHERE ChannelId NOT IN (SELECT Channelid FROM ChannelTable))
You need to use NOT EXISTS twice
AND NOT EXISTS (
SELECT 1
FROM VariationProduct VP
WHERE VP.ProductID = bvc_Product.ProductID
AND NOT EXISTS( SELECT 1 FROM ChannelReport WHERE ProductID = VP.ChildProductID )
)

Select unique record based on column value priority

This is a continuation of my previous question here.
In the following example:
id PRODUCT ID COLOUR
1 1001 GREEN
2 1002 GREEN
3 1002 RED
4 1003 RED
Given a product ID, I want to retrieve only one record - that with GREEN colour, if one exists, or the RED one otherwise. It sounds like I need to employ DISTINCT somehow, but I don't know how to supply the priority rule.
Pretty basic I'm sure, but my SQL skills are more than rusty..
Edit: Thank you everybody. One more question please: how can this be made to work with multiple records, ie. if the WHERE clause returns more than just one record? The LIMIT 1 would limit across the entire set, while what I'd want would be to limit just within each product.
For example, if I had something like SELECT * FROM table WHERE productID LIKE "1%" ... how can I retrieve each unique product, but still respecting the colour priority (GREEN>RED)?
try this:
SELECT top 1 *
FROM <table>
WHERE ProductID = <id>
ORDER BY case when colour ='GREEN' then 1
when colour ='RED' then 2 end
If you want to order it based on another color, you can give it in the case statement
SELECT *
FROM yourtable
WHERE ProductID = (your id)
ORDER BY colour
LIMIT 1
(Green will come before Red, you see. The LIMIT clause returns only one record)
For your subsequent edit, you can do this
select yourtable.*
from
yourtable
inner join
(select productid, min(colour) mincolour
from yourtable
where productid like '10%'
group by productid) v
on yourtable.productid=v.productid
and yourtable.colour=v.mincolour