Select if then case with first record - sql

Can you do something like this in SQL Server?
I want to select from a table which has some records with the same product_id in one column and a Y or N in another (in stock), and take the first one which has a Y where the product_id is the same, while matching the product_id_set from another table.
... ,
SELECT
(SELECT TOP 1
(product_name),
CASE
WHEN in_stock = 'Y' THEN product_name
ELSE product_name
END
FROM
Products
WHERE
Products.product_set = Parent_Table.product_set) AS 'Product Name',
...
Sample data would be
product_set in_stock product_id product_name
---------------------------------------------------
1 N 12 Orange
1 Y 12 Pear
2 N 12 Apple
2 N 12 Lemon
Output from product_set = 1 would be 'Pear' for example.

So there's kind of two solutions depending on the answer to the following question. If there are no records for a product id with an in_stock value of 'Y', should anything return? Secondly, if there are multiple rows with in_stock 'Y', do you care which one it picks?
The first solution assumes you want the first row, whether or not there is ANY "Y" value.
select *
from (select RID = row_number() over (partition by product_set order by in_stock desc) -- i.e. sort Y before N
from Products) a
where a.RID = 1
The second will only return a value if there is at least one row with a 'Y' for in_stock. Note that the order by (select null) is essentially saying you don't care which one it picks if there are multiple in_stock items. If you DO care the order, replace it with the appropriate sort condition.
select *
from (select RID = row_number() over (partition by product_set order by (select null)) -- i.e. sort Y before N
from Products
where in_stock = 'Y') a
where a.RID = 1
I don't know what the structure of the "parent table" in your query is, so I've simplified it to assume you have what you need in Products alone.

SELECT ISNULL(
(
SELECT TOP 1 product_name
FROM Products
WHERE Products.product_set = Parent_Table.product_set
AND Products.in_stock = 'Y'
), 'Not in the stock') AS 'Product Name'

Related

how can I count some values for data in a table based on same key in another table in Bigquery?

I have one table like bellow. Each id is unique.
id
times_of_going_out
fef666
2
S335gg
1
9a2c50
1
and another table like this one ↓. In this second table the "id" is not unique, there are different "category_name" for a single id.
id
category_name
city
S335gg
Games & Game Supplies
tk
9a2c50
Telephone Companies
os
9a2c50
Recreation Centers
ky
fef666
Recreation Centers
ky
I want to find the difference between destinations(category_name) of people who go out often(times_of_going_out<5) and people who don't go out often(times_of_going_out<=5).
** Both tables are a small sample of large tables.
 ・ Where do people who go out twice often go?
 ・ Where do people who go out 6times often go?
Thank you
The expected result could be something like
less than 5
more than 5
top ten “category_name” for uid’s with "times_of_going_out" less than 5 times
top ten “category_name” for uid’s with "times_of_going_out" more than 5 times
Steps:
combining data and aggregating total time_going_out
creating the categories that you need : less than equal to 5 and more than 5. if you don't need equal to 5, you can adjust the code
ranking both categories with top 10, using dense_rank(). this will produce the rank from 1 - 10 based on the total time_going out
filtering the cases so it takes top 10 values for both categories
with main as (
select
category_name,
sum(coalesce(times_of_going_out,0)) as total_time_per_category
from table1 as t1
left join table2 as t2
on t1.id = t2.id
group by 1
),
category as (
select
*,
if(total_time_per_category >= 5, 'more than 5', 'less than equal to 5') as is_more_than_5_times
from main
),
ranking_ as (
select *,
case when
is_more_than_5_times = 'more than 5' then
dense_rank() over (partition by is_more_than_5_times order by total_time_per_category desc)
else NULL
end AS rank_more_than_5,
case when
is_more_than_5_times = 'less than equal to 5' then
dense_rank() over (partition by is_more_than_5_times order by total_time_per_category)
else NULL
end AS rank_less_than_equal_5
from category
)
select
is_more_than_5_times,
string_agg(category_name,',') as list
from ranking_
where rank_less_than_equal_5 <=10 or rank_more_than_5 <= 10
group by 1

Create a List of Products which miss a specific Version

I have to create a list of products from ProductTable. The field which I am filtering is the VersionID.
So I have a Table of ProductIDs and VersionIDs. The Problem is that for every Version there is an entry in the database e.g.
ProductID | VersionID
1 | x
1 | y
1 | z
2 | y
The Product which contains Version x should not show up on this list, so in this Case only Product 2 should be listed.
Thanks for your help in advance!
you could try using a subquery where you get all ProdcutID that you dont want
select ProdcutID from PRODUCTTABLE where VersionID = x
and filter your query with not in subquery like this
select *
from PRODUCTTABLE
where ProdcutID not in (select ProdcutID
from PRODUCTTABLE
where VersionID = x)
You can try the following query:
SELECT *
FROM ProductTable p
WHERE NOT EXISTS (SELECT * FROM ProductTable p2 WHERE p2.ProductId = p.ProductId AND Version = x)
In a nutshell we select all products where another query for the same product -- check out where we use p and p2 -- with a specific version x does not yield a result.
You can do it in a single pass (no sub-query, etc), by aggregating and checking the contents of the aggregate don't contain an 'x'
SELECT
ProductID
FROM
ProductTable
GROUP BY
ProductID
HAVING
MAX(CASE WHEN VersionID = 'x' THEN 1 ELSE 0 END) = 0

SQl - Select best price based on quantity

I have a table that contains prices for a particular item based upon the quantity being ordered and the type of client placing the order ...
ID Name Quantity ClientType Price/Unit ($)
========================================================
1 Cheese 10 Consumer 20
2 Cheese 20 Consumer 15
3 Cheese 30 Consumer 12
4 Cheese 10 Restaurant 18
5 Cheese 20 Restaurant 13
6 Cheese 30 Restaurant 10
I have having trouble with WHERE clause in the SQL to select the row where the customer gets the best price based upon the quantity that is ordered. The rule is they must at least meet the quantity in order to get the price for that pricing tier. If their order is below the minimum quantity then they get the Price for the first quantity (10 in this case) and if they order more than the largest quantity (30 in this example) they get that price.
For example ... If a Restaurant orders 26 units of cheese the row with ID = 5 should be chosen. If a Consumer ordered 9 units of cheese then the row returned should be ID = 1. If the Consumer orders 50 units of cheese then they should get ID = 3.
declare #SelectedQuantity INT;
SELECT *
FROM PriceGuide
WHERE Name = 'Cheese'
AND ClientType = 'Consumer'
AND Quantity <= #SelectedQuantity
What am I missing in the WHERE clause?
Edit
The first solution didn't handle the special case correctly, as mentioned in the comments.
Next try:
SELECT TOP 1 ID, Name, Quantity, ClientType, [Price/Unit]
FROM PriceGuide
WHERE Name = 'Cheese'
AND ClientType = 'Consumer'
ORDER BY CASE WHEN Quantity <= #SelectedQuantity THEN Quantity ELSE -Quantity END DESC
Assuming that Quantity is positive, the ORDER BY will return rows that meet Quantity <= #SelectedQuantity condition first, in a descending order.
For rows that do not match this condition, it uses -Quantity for ordering. So if no rows match the condition, the one with smallest quantity will be returned.
This is a little tricky because you need to deal with the quantities less than 10.
I think the best approach is:
SELECT TOP 1 *
FROM PriceGuide
WHERE Name = 'Cheese' AND ClientType = 'Consumer'
ORDER BY (CASE WHEN #SelectedQuantity >= Quantity THEN 1 ELSE 0 END) DESC,
(CASE WHEN #SelectedQuantity >= Quantity THEN PriceUnit END) ASC,
Quantity ASC;
This version handles the minimum quantity by keeping all the rows for a given Name/ClientType, using the ORDER BY for prioritization.
Your query will return all matching rows less than the #SelectedQuantity. You only want to return the row with highest quantity less than the selected quantity, so a subquery is needed to get this result:
SELECT *
FROM PriceGuide a
WHERE a.Name = 'Cheese'
AND a.ClientType = 'Consumer'
AND a.Quantity = (SELECT MAX(Quantity) FROM PriceGuide
WHERE ClientType = a.ClientType
AND Name = a.Name
AND Quantity <= #SelectedQuantity)
In your example of 26 unit, your query will return row 4 and 5 whereas you need it to return only row 5.
declare #SelectedQuantity INT;
SELECT *
FROM PriceGuide
WHERE Name = 'Cheese'
AND ClientType = 'Consumer'
AND Quantity <= #SelectedQuantity
ORDER BY Quantity DESC
LIMIT 1

How to select or skip to the next row if meets certain criteria

I really am not even sure which direction to go with this...
I'm trying to select a list of customers based on the following rules:
Select all rows from Customer where Ranking = 1,
OR if Ranking = 1 AND Type = Store then Rank 1 and return the row with Rank 2.
OR if the customer only has 1 row, return it even if the type = Store.
The Ranking is not assigned with a Rank statement in the query. Rather it is an actual column in the Customer table (populated by a stored proc that does the ranking).
Using the example below I'd want rows 1, 4, 6, and 10 returned.
Customer Table
RowID CustID Type Ranking
----- ------ ---- -------
1 9 Web 1
2 9 Catalog 2
3 9 Store 3
4 10 Store 1
5 11 Store 1
6 11 Web 2
7 12 Store 1
8 12 Web 2
9 12 Catalog 3
10 13 Web 1
I feel like this task is more difficult BECAUSE the Ranking is already done when the table is created! Any suggestions are most welcome!
You could try something like this (I haven't tested it!):
SELECT
RowId,
CustId,
Type,
Ranking
FROM Customer c
WHERE (c.Ranking = 1 AND c.Type != 'Store')
OR (c.Type = 'Store' AND Ranking = 2)
OR (c.Type = 'Store' AND Ranking = 1 AND NOT EXISTS (SELECT 1 FROM Customer WHERE CustId = c.CustId AND Ranking = 2))
If the customer table is large, you might find that the query is a bit slow and something like this would be faster:
SELECT
RowId,
CustId,
Type,
Ranking
FROM Customer c
WHERE c.Ranking = 1 AND c.Type != 'Store'
UNION ALL
SELECT
RowId,
CustId,
Type,
Ranking
FROM Customer c
WHERE c.Type = 'Store' AND Ranking = 2
UNION ALL
SELECT
RowId,
CustId,
Type,
Ranking
FROM Customer c
WHERE c.Type = 'Store' AND Ranking = 1 AND NOT EXISTS (SELECT 1 FROM Customer WHERE CustId = c.CustId AND Ranking = 2)
As with the other answer, I haven't done a lot of thorough testing, but here's what I'd look at. The idea here is to build a row_number over the data set prioritizing type:store to the top, and then using rank as the secondary sort condition.
select *
from (
select
rid = row_number() over (partition by CustID, order by case when type = 'Store' then 0 else 1 end, Rank desc),
rowid,
CustID,
Type,
Ranking
from customer)
where RID = 1
Try:
SELECT *
FROM Customer c
WHERE
-- There's only one row for this customer
(
SELECT COUNT(*)
FROM Customer
WHERE CustID = c.CustID
) = 1
-- There's a row with Ranking = 1 and Type = 'Store', so select Ranking = 2
OR (Ranking = 2 AND EXISTS (SELECT 1 FROM Customer WHERE CustID = c.CustID AND Ranking = 1 AND Type = 'Store'))
-- There's a row with Ranking = 1 that's not Type = 'Store'
OR (Ranking = 1 AND Type <> 'Store')

showing items when 'x' is present

Ok, so hoping I can get some help here after searching with no joy.
So I have a key 'orderno' and each 'orderno' has multiple items. Each item has a status. I want to pull a Q that shows only the orderno's that contain an item that has status of 'x'
So If there are 3 items and only 1 is showing status 'x' I want to see all three items not just the one.
Essentially removing any order/items that do not show the x value.
So table1
orderno / Itemno / Itemstatus
1 1 y
1 2 x
2 1 z
3 1 y
3 2 x
3 3 y
4 1 y
4 1 y
EDIT:
So basically the letters represent open, closed, or inprogress... I want to see only order that have and item closed as well as an item in progress so I can see why the order is only showing partially complete from there. Still probably not making sense here. Grrrr.
I need to return the ORDER# and all item#'s for any order that contains an item with status of 'x'.
SELECT * FROM Order_Table
WHERE orderno IN
(SELECT orderno FROM Order_Table WHERE Itemstatus = 'x')
The Inner query returns all the orders with the status 'x' and the outer one return all details of those orders.
I prefer EXISTS to the IN or JOIN versions. It general faster.
Added a sqlfiddle.
CREATE TABLE table1(orderno INT, Itemno INT, Itemstatus CHAR(1))
INSERT INTO table1 VALUES
(1,1,'y')
,(1,2,'x')
,(2,1,'z')
,(3,1,'y')
,(3,2,'x')
,(3,3,'y')
,(4,1,'y')
,(4,1,'y')
SELECT *
FROM table1 a
WHERE EXISTS(SELECT 1
FROM table1 b
WHERE b.OrderNo = a.OrderNo
AND b.Itemstatus='x')