I'm trying to query, possibly with a self-join, to find which Products are on sale in Chicago that are not on sale in Miami.
ProductID Product_Type On Sale Stock_location
2201 Cereal Y chicago
2202 Beverage Y chicago
2203 Frozen Food y chicago
2204 Poultry N chicago
2205 Health N chicago
2206 Snacks Y chicago
2207 Household N chicago
2208 Personal N chicago
2209 Produce N chicago
2201 Cereal Y Miami
2202 Beverage Y Miami
2203 Frozen Food N Miami
2204 Poultry Y Miami
2205 Health N Miami
2206 Snacks Y Miami
2207 Household N Miami
2208 Personal N Miami
It's something like below
select Product_Type from Products
where [On Sale] = 'Y' and
Stock_location = 'chicago' and
Product_Type not in (Select Product_Type form Products where [On Sale] = 'N' and Stock_location = 'Miami' )
SELECT t1.*
FROM t AS t1
JOIN t AS t2 ON ( t1.Product_Type = t2.Product_Type )
WHERE t1.On_Sale = "Y"
AND t2.On_Sale = "N";
Useful resource:
https://www.w3schools.com/sql/sql_join_self.asp
Related
I have 2 Tables (History and Responsible). They need to be JOINED based on Service Date.
History Table:
Id
ServiceDate
Hours
ClientId
ClientName
1
2021-10-15
3
123
Tom Holland
2
2021-10-25
5
123
Tom Holland
3
2022-01-14
2
123
Tom Holland
Responsible Table:
2999-12-31 means Responsible has no end date (current)
ClientId
ClientName
ResponsibleId
ResponsibleName
ResponsibleStartDate
ResponsibleEndtDate
123
Tom Holland
77
Thomas Anderson
2020-09-17
2021-10-17
123
Tom Holland
88
Tom Cruise
2021-10-18
2999-12-31
123
Tom Holland
99
Sten Lee
2022-01-07
2999-12-31
My code produces multiple rows, because 2022-01-14 Service date falls under multiple date ranges from Responsible Table:
SELECT h.Id,
h.ServiceDate,
h.Hours,
h.ClientId,
h.ClientName,
r.ResponsibleName
FROM History AS h
LEFT JOIN Responsible AS r
ON (h.ClientId = r.ClientId AND h.ServiceDate BETWEEN r.ResponsibleStartDate AND r.ResponsibleEndtDate)
The output of the query above is:
Id
ServiceDate
Hours
ClientId
ClientName
ResponsibleName
1
2021-10-15
3
123
Tom Holland
Thomas Anderson
2
2021-10-25
5
123
Tom Holland
Tom Cruise
3
2022-01-14
2
123
Tom Holland
Tom Cruise
3
2022-01-14
2
123
Tom Holland
Sten Lee
Technically, output is correct (because 2022-01-14 is between 2021-10-18 - 2999-12-31 as well between 2022-01-07 - 2999-12-31), but not what I need.
I would like to know if possible to achieve 2 outputs:
1) If Service Date falls in multiple date ranges from Responsible Table, Responsible Should be the person who's ResponsibleStartDate is closer to the ServiceDate:
Id
ServiceDate
Hours
ClientId
ClientName
ResponsibleName
1
2021-10-15
3
123
Tom Holland
Thomas Anderson
2
2021-10-25
5
123
Tom Holland
Tom Cruise
3
2022-01-14
2
123
Tom Holland
Sten Lee
2) Keep all rows, if Service Date falls in multiple date ranges from Responsible Table, but split Hours evenly between Responsible:
Id
ServiceDate
Hours
ClientId
ClientName
ResponsibleName
1
2021-10-15
3
123
Tom Holland
Thomas Anderson
2
2021-10-25
5
123
Tom Holland
Tom Cruise
3
2022-01-14
1
123
Tom Holland
Tom Cruise
3
2022-01-14
1
123
Tom Holland
Sten Lee
First one, we can use a window function to apply a row number, based on how close to ServiceDate the ResponsibleStartDate is, then we can just pick the first row per h.Id. If there is a tie we can break it by picking something that will give us deterministic order, e.g. ORDER BY {DATEDIFF expression}, ResponsibleName.
;WITH cte AS
(
SELECT h.Id,
h.ServiceDate,
h.Hours,
h.ClientId,
h.ClientName,
r.ResponsibleName,
RankOrderedByProximityToServiceDate = ROW_NUMBER() OVER
(PARTITION BY h.Id
ORDER BY ABS(DATEDIFF(DAY, ResponsibleStartDate, ServiceDate)))
FROM dbo.History AS h
LEFT JOIN dbo.Responsible AS r
ON (h.ClientId = r.ClientId
AND h.ServiceDate BETWEEN r.ResponsibleStartDate AND r.ResponsibleEndtDate)
)
SELECT Id, ServiceDate, Hours, ClientId, ClientName, ResponsibleName
FROM cte WHERE RankOrderedByProximityToServiceDate = 1;
Output:
Id
ServiceDate
Hours
ClientId
ClientName
ResponsibleName
1
2021-10-15
3
123
Tom Holland
Thomas Anderson
2
2021-10-25
5
123
Tom Holland
Tom Cruise
3
2022-01-14
2
123
Tom Holland
Sten Lee
Second one doesn't require a CTE, we can simply divide the Hours in h by the number of rows that exist for that h.Id, then limit it to 2 decimal places:
SELECT h.Id,
h.ServiceDate,
Hours = CONVERT(decimal(11,2),
h.Hours * 1.0
/ COUNT(h.Id) OVER (PARTITION BY h.Id)),
h.ClientId,
h.ClientName,
r.ResponsibleName
FROM dbo.History AS h
LEFT JOIN dbo.Responsible AS r
ON (h.ClientId = r.ClientId
AND h.ServiceDate BETWEEN r.ResponsibleStartDate AND r.ResponsibleEndtDate);
Output:
Id
ServiceDate
Hours
ClientId
ClientName
ResponsibleName
1
2021-10-15
3.00
123
Tom Holland
Thomas Anderson
2
2021-10-25
5.00
123
Tom Holland
Tom Cruise
3
2022-01-14
1.00
123
Tom Holland
Tom Cruise
3
2022-01-14
1.00
123
Tom Holland
Sten Lee
Both demonstrated in this db<>fiddle.
My attempt at part 1 - it doesn't work if there's more than one Responsible as of the same start date.
WITH
"all_services" AS (
SELECT
h.Id,
h.ServiceDate,
h.Hours,
h.ClientId,
h.ClientName,
r.ResponsibleName,
r.ResponsibleStartDate
FROM History AS h
LEFT JOIN Responsible AS r
ON h.ClientId = r.ClientId
AND h.ServiceDate BETWEEN r.ResponsibleStartDate AND r.ResponsibleEndtDate
),
"most_recent_key" AS (
SELECT
ServiceDate,
ClientId,
MAX(ResponsibleStartDate) AS "ResponsibleStartDate"
FROM all_services
GROUP BY ServiceDate, ClientId
)
SELECT Id, ServiceDate, Hours, ClientId, ClientName, ResponsibleName
FROM all_services
INNER JOIN most_recent_key
USING (ServiceDate, ClientId, ResponsibleStartDate)
Posting it anyway as a contrast to Aaron's better solution as a learning point for myself.
I have got a client table which pretty much looks like below:
Client List
customer no. Customer name
123 Kristen Smith
128 Jeremy Church
127 Alan Li
132 Ryan Nelson
I need to map it to a Customer_Dim table
Customer_Dim
customer no. Customer name Group no. Group Name Cust_Active Flag
123 Kristen Smith 5491 Zealong Tea Estate Y
167 Anna Hathaway 5823 AA Insurance Y
146 Simon Joe 5671 Direct Automobile Y
148 Henry Wilson 5823 AA Insurance Y
195 Graham Brown 5491 Zealong Tea Estate Y
172 Daria Smith 5671 Direct Automobile N
122 Dyana Smith 5823 AA Insurance N
132 Ryan Nelson 5671 Direct Automobile N
128 Jeremy Church 5823 AA Insurance Y
127 Alan Li 5671 Direct Automobile Y
to get their group numbers from below table (which I am able to do by a simple left join)
to list all the remaining customers (who are active) from the group numbers of the client customer [I AM UNABLE TO DO THIS 2nd PART] :
Required Results :
Customer No. Customer name Group No. Group Name
123 Kristen Smith 5491 Zealong Tea Estate
128 Jeremy Church 5823 AA Insurance
127 Alan Li 5671 Direct Automobile
195 Graham Brown 5491 Zealong Tea Estate
167 Anna Hathaway 5823 AA Insurance
148 Henry Wilson 5823 AA Insurance
146 Simon Joe 5671 Direct Automobile
Please let me know if any other information is needed.
Sorry, if a similar question has been asked earlier - did several searches but was unable to find anything.
Thanks
join the tables to get all the group numbers of the clients in the client list and then select from customer_dim only the clients of these group numbers who are active:
select * from customer_dim
where
cust_active_flag = 'Y'
and
groupno in (
select groupno
from client_list l inner join customer_dim d
on d.customerno = l.customerno
)
See the demo.
Results:
> customerno | customername | groupno | groupname | cust_active_flag
> ---------: | :------------ | ------: | :----------------- | :---------------
> 123 | Kristen Smith | 5491 | Zealong Tea Estate | Y
> 167 | Anna Hathaway | 5823 | AA Insurance | Y
> 146 | Simon Joe | 5671 | Direct Automobile | Y
> 148 | Henry Wilson | 5823 | AA Insurance | Y
> 195 | Graham Brown | 5491 | Zealong Tea Estate | Y
> 128 | Jeremy Church | 5823 | AA Insurance | Y
> 127 | Alan Li | 5671 | Direct Automobile | Y
for get required results you need a condition in your join
SELECT *
FROM Client c
JOIN Customer_Dim cd on c.CustomerNo = cd.CustomerNo
and cd.Cust_ActiveFlag ='Y'
or
SELECT *
FROM Client c
JOIN Customer_Dim cd on c.CustomerNo = cd.CustomerNo
WHERE cd.Cust_ActiveFlag ='Y'
I think it is pretty simple to get your posted result from Customer_Dim table.
if you don't want Group No. of ClientList
select * from Customer_Dim
where [Cust_Active Flag] = 'Y'
and [Group No.] not in (
select CD.[Group No.] from [Client List] as CL inner join Customer_Dim as CD where CL.[customer no.] = CD.[customer no.] )
And
if you only want Group No. of ClientList
select * from Customer_Dim
where [Cust_Active Flag] = 'Y'
and [Group No.] in (
select CD.[Group No.] from [Client List] as CL inner join Customer_Dim as CD where CL.[customer no.] = CD.[customer no.] )
For the case of a client being inactive and wanting to identify the client in the result set you can do a LEFT JOIN with a GROUP BY and leverage Proc SQL automatic remerging in a HAVING clause for selection criteria.
data client_list; input
custno custname:& $30.; datalines;
123 Kristen Smith
128 Jeremy Church
127 Alan Li
132 Ryan Nelson
899 Julius Caesar
run;
data customer_dim; input
custno custname:& $30. groupnum groupname:& $30. Cust_Active_Flag: $1.; datalines;
123 Kristen Smith 5491 Zealong Tea Estate Y
167 Anna Hathaway 5823 AA Insurance Y
146 Simon Joe 5671 Direct Automobile Y
148 Henry Wilson 5823 AA Insurance Y
195 Graham Brown 5491 Zealong Tea Estate Y
172 Daria Smith 5671 Direct Automobile N
122 Dyana Smith 5823 AA Insurance N
132 Ryan Nelson 5671 Direct Automobile N
128 Jeremy Church 5823 AA Insurance Y
127 Alan Li 5671 Direct Automobile Y
231 Donald Duck 7434 Orange Insurance Y
899 Julius Caesar 4999 Emperors N
900 Joshua Norton 4999 Emperors N
925 Joaquin Guzman 4999 Emperors Y
925 Naruhito 4999 Emperors Y
run;
proc sql;
create table want(label="Active customers of clients groups") as
select
LIST.custno as client,
DIM.*
from
customer_dim DIM
left join
client_list LIST
on
DIM.custno = LIST.custno
group by
groupnum
having
N(LIST.custno) > 0
and
(
cust_active_flag = 'Y'
or LIST.custno is not NULL
)
order by
groupnum, custno
;
I have a oracle view, and include the sale of goods details. It like this:
OrderId OrderDetailId GoodsId GoodsName UnitName SalesQty Price CustomerName Country City
200138 ddd0a3b42adb 770 A bag 5 18.00 AAAA USA NewYork
223448 70ca7ceb41c7 193 D bottle 10 10.00 BBB USA NewYork
200118 ab472857573e 1286 F cup 8 50.00 CCC China Beijing
244028 230a43920667 770 A bag 20 18.00 CCC China Beijing
251118 118fc2b3839b 5929 C box 40 6.00 DDDD Japan Tokyo
200000 abd0a3b42ddd 770 A bag 15 18.00 AAAA USA NewYork
111118 11111113839b 5929 C box 40 6.00 FFFF Japan Tokyo
And I want a SQL statement statistics sales, the number of cities, the number of customers of each goods. The result should be like this:
GoodsId GoodsName SalesQty(UnitName) CityQty CustomerQty
GoodsId GoodsName SalesQty(UnitName) CityQty CustomerQty
770 A 40(bag) 2 2
193 D 10(bottle) 1 1
1286 F 8(cup) 1 1
5929 C 80(box) 1 2
How to write sql statistical statement? Thanks!
Simply use GROUP BY
select GoodsId, GoodsName, UnitName,
sum(SalesQty) SalesQty,
count(distinct City) CityQty,
count(distinct CustomerName) CustomerQty
from goods_view
group by GoodsId, GoodsName, UnitName
demo
You may use cube, to include also sub&grand totals of quantities :
select GoodsId, GoodsName||'('||UnitName||')' GoodsName,
sum(SalesQty) SalesQty, count(distinct City) CityQty, count(distinct CustomerName) CustomerQty
from v_goods
group by cube(GoodsId, GoodsName||'('||UnitName||')')
order by GoodsId,GoodsName||'('||UnitName||')';
I have taken the chance to learn sql. I have a basic table named book. I have been presented with the scenario: display the book category, and the number of
books in each book category for books with an average price greater than 15
dollars. My query below is not working. How can i count each book type and get the average price to see if it greater than 15?
Query
SELECT type FROM books WHERE COUNT(DISTINCT type) * SUM(price) > 15;
Table Schema
Select * From Book;
BOOK TITLE PUB TYP PRICE P
---- ---------------------------------------- --- --- ---------- -
0180 A Deepness in the Sky TB SFI 7.19 Y
0189 Magic Terror FA HOR 7.99 Y
0200 The Stranger VB FIC 8 Y
0378 Venice SS ART 24.5 N
079X Second Wind PU MYS 24.95 N
0808 The Edge JP MYS 6.99 Y
1351 Dreamcatcher: A Novel SC HOR 19.6 N
1382 Treasure Chests TA ART 24.46 N
138X Beloved PL FIC 12.95 Y
2226 Harry Potter and the Prisoner of Azkaban ST SFI 13.96 N
2281 Van Gogh and Gauguin WP ART 21 N
2766 Of Mice and Men PE FIC 6.95 Y
2908 Electric Light FS POE 14 N
3350 Group: Six People in Search of a Life BP PSY 10.4 Y
3743 Nine Stories LB FIC 5.99 Y
3906 The Soul of a New Machine BY SCI 11.16 Y
5163 Travels with Charley PE TRA 7.95 Y
5790 Catch-22 SC FIC 12 Y
6128 Jazz PL FIC 12.95 Y
6328 Band of Brothers TO HIS 9.6 Y
669X A Guide to SQL CT CMP 37.95 Y
6908 Franny and Zooey LB FIC 5.99 Y
7405 East of Eden PE FIC 12.95 Y
7443 Harry Potter and the Goblet of Fire ST SFI 18.16 N
7559 The Fall VB FIC 8 Y
8092 Godel, Escher, Bach BA PHI 14 Y
8720 When Rabbit Howls JP PSY 6.29 Y
9611 Black House RH HOR 18.81 N
9627 Song of Solomon PL FIC 14 Y
9701 The Grapes of Wrath PE FIC 13 Y
9882 Slay Ride JP MYS 6.99 Y
9883 The Catcher in the Rye LB FIC 5.99 Y
9931 To Kill a Mockingbird HC FIC 18 N
When you want to have the aggregations in the filter then you should GROUP BY and then use the HAVING clause.
SELECT type FROM books
GROUP BY type
HAVING COUNT(*) * SUM(price) > 15;
I am using OE schema and trying to see item # and quantity on hand in each warehouse and if any warehouse does not have item than it should show 0 item in hand. i am running following SQL and it is not showing 0 quantity for items.
select i.product_id,w.warehouse_name ,(i.quantity_on_hand)
from inventories i
right outer join warehouses w
on (i.warehouse_id=w.warehouse_id)
order by 1
I want to see result like this:
PRODUCT_ID WAREHOUSE_NAME NVL(I.QUANTITY_ON_HAND,0)
---------- ----------------------------------- -------------------------
2262 Sydney 35
2262 Beijing 50
2262 Bombay 35
2262 San Francisco 155
2262 Seattle, Washington 77
Toronto 0
New Jersey 0
Southlake, Texas 0
Mexico City 0
3501 Toronto 220
3501 Sydney 320
3501 Mexico City 294
3501 Beijing 268
3501 San Francisco 353
New Jersey 0
Southlake, Texas 0
Seattle, Washington 0
Bombay 0
You want to see a row for every warehouse and every product. So, start with generating this list and use left outer join to bring in the values that exist:
select i.product_id, w.warehouse_name, coalesce(i.quantity_on_hand, 0)
from warehouses w cross join
(select distinct product_id from inventories) p left join
inventories i
on w.warehouse_id = i.warehouse_id and p.product_id = i.product_id
order by i.product_id, w.warehouse_name;