I have a purchases table that looks like this:
store_id. industry_code amt_age_18_24 amt_age_25-34 amt_men amt_women
1 1000 100 20 80 40
2 2000 100 100 130 70
What I'm trying to do is find every permutation of purchases by age and gender for each store. Something like this, where each row is unique:
store_id. industry_code amt_age_18_24 amt_age_25-34 amt_men amt_women
1 1000 100 NULL 80 NULL
1 1000 100 NULL NULL 40
1 1000 NULL 20 80 NULL
1 1000 NULL 20 NULL 80
2 2000 100 NULL 130 NULL
2 2000 100 NULL NULL 70
2 2000 NULL 100 130 NULL
2 2000 NULL 100 NULL 70
What's the best way to do this? A self join?
This looks like union all:
select store_id, instrustry_code, amt_age_18_24, null as amt_age_25_34, amt_men, null as amt_women
from t
union all
select store_id, instrustry_code, amt_age_18_24, null as amt_age_25_34, null as amt_men, amt_women
from t
union all
. . .
Here is an approach using a cross join with a derived table that contains "column masks":
select
t.store_id,
t.industry_code,
t.amt_age_18_24 * x.amt_age_18_24 as amt_age_18_24,
t.amt_age_25_34 * x.amt_age_25_34 as amt_age_25_34,
t.amnt_men * x.amnt_men as amnt_men,
t.amt_women * x.amt_women as amt_women
from mytable t
cross join (
select 1 as amt_age_18_24, null as amt_age_25_34, 1 as amnt_men, null as amt_women
union all select 1, null, null, 1
union all select null, 1, 1, null
union all select null, 1, null, 1
) x
The upside is that this does not require scanning the table multiple times, as opposed to the union all approach.
You can use union for each permutation as you wish:
select store_id, instrustry_code, amt_age_18_24, null as amt_age_25_34, amt_men, null as amt_women
from t
union all
select store_id, instrustry_code, amt_age_18_24, null as amt_age_25_34, null as amt_men, amt_women
from t
and do it for as many columns as you want
Related
I am attempting to gap fill in two scenarios. I can do it with one group but am uncertain with multiple
the data:
Order ID Amount
1 NULL NULL
2 A 500
3 NULL NULL
4 A 700
1 B 1000
2 NULL NULL
3 NULL NULL
4 B 1500
Target Result
Order ID Amount
1 A 500
2 A 500
3 A 700
4 A 700
1 B 1000
2 B 1500
3 B 1500
4 B 1500
Consider below approach
select * except(amount),
first_value(amount ignore nulls) over win as amount
from (select distinct `order` from your_table where not `order` is null),
(select distinct id from your_table where not id is null)
left join your_table using(`order`, id)
window win as (partition by id order by `order` rows between current row and unbounded following)
if applied to sample data in your question - output is
I have a table where I have all the customers and a table where I have all their restrictions.
CUSTOMER
customer_id customer_name
1 name 1
2 name 2
CUSTOMER_RESTRICTIONS
rest_type day_of_week hour_start hour_stop customer_id
TYPE1 0 08:00 12:00 1
TYPE1 0 13:00 17:00 1
TYPE2 0 17:00 23:59 1
Problem: I only have a record for a restriction type and a customer when the customer has a restriction and this is a problem in the visualization I want to build.
I need every customer, every day and every restriction type, even when there is no restriction. In that case hour_start and hour_stop would be NULL.
For the tables shown, the output would be
rest_type day_of_week hour_start hour_stop customer_id
TYPE1 0 08:00 12:00 1
TYPE1 0 08:00 12:00 1
TYPE1 1 NULL NULL 1
TYPE1 2 NULL NULL 1
TYPE1 3 NULL NULL 1
TYPE1 4 NULL NULL 1
TYPE1 5 NULL NULL 1
TYPE1 6 NULL NULL 1
TYPE1 1 NULL NULL 1
TYPE1 2 NULL NULL 1
TYPE1 3 NULL NULL 1
TYPE1 4 NULL NULL 1
TYPE1 5 NULL NULL 1
TYPE2 0 NULL NULL 1
TYPE2 1 NULL NULL 1
TYPE2 2 NULL NULL 1
TYPE2 3 NULL NULL 1
TYPE2 4 NULL NULL 1
TYPE2 5 NULL NULL 1
TYPE2 6 NULL NULL 1
TYPE1 0 NULL NULL 2
TYPE1 1 NULL NULL 2
TYPE1 2 NULL NULL 2
TYPE1 3 NULL NULL 2
TYPE1 4 NULL NULL 2
TYPE1 5 NULL NULL 2
TYPE1 6 NULL NULL 2
TYPE2 0 NULL NULL 2
TYPE2 1 NULL NULL 2
TYPE2 2 NULL NULL 2
TYPE2 3 NULL NULL 2
TYPE2 4 NULL NULL 2
TYPE2 5 NULL NULL 2
TYPE2 6 NULL NULL 2
How can I achieve that? I couldn't even start to build this query.
Essentially you need to start with the data you must have and left join the optional data. E.g., something like this:
select c.customer_id
,r.[rest_type]
,d.[day_of_week]
,r.[hour_start]
,r.[hour_stop]
from CUSTOMER c
cross apply (
select 0 as day_of_week
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
) d
left join CUSTOMER_RESTRICTIONS r on c.customer_id = r.customer_id and d.day_of_week = r.day_of_week
Output:
customer_id rest_type day_of_week hour_start hour_stop
----------- --------- ----------- ---------- ---------
1 TYPE1 0 08:00 12:00
1 TYPE1 0 13:00 17:00
1 TYPE2 0 17:00 23:59
1 NULL 1 NULL NULL
1 NULL 2 NULL NULL
1 NULL 3 NULL NULL
1 NULL 4 NULL NULL
1 NULL 5 NULL NULL
1 NULL 6 NULL NULL
If there are only type rest_types, you don't have a lookup table for them, and you want to show a row for each, you would do:
select c.customer_id
,t.[rest_type]
,d.[day_of_week]
,r.[hour_start]
,r.[hour_stop]
from CUSTOMER c
cross apply (
select 0 as day_of_week
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
) d
cross apply (
select 'TYPE1' as rest_type
union all select 'TYPE2'
) t
left join CUSTOMER_RESTRICTIONS r on c.customer_id = r.customer_id
and d.day_of_week = r.day_of_week
and t.rest_type = r.rest_type
(select rest_type, day_of_week,
hour_start ,
hour_stop
from table A
where rest_type IS NOT NULL)
Union
(select rest_type,
day_of_week,
NULL ,NULL
from table A
where rest_type IS NULL)
Is this what you want ?
First off, I wouldn't store rest type as you are, that is a bad habit, it should be a reference table!
You need to cross apply to get all your possible combinations, and then add in the values you DO have...
DECLARE #Customer TABLE (Id INT IDENTITY(1,1), Name NVARCHAR(100))
DECLARE #Rest TABLE (Id INT IDENTITY(1,1), Name NVARCHAR(100))
DECLARE #Restrictions TABLE (Id INT IDENTITY(1,1), RestID INT, CustomerID INT, Day_of_Week TINYINT, hour_start TIME, hour_end TIME)
INSERT INTO #Customer (NAME)
VALUES('JOHN'),('SUSAN')
INSERT INTO #Rest (NAME)
VALUES ('TYPE A'),('TYPE B')
INSERT INTO #Restrictions (RestID,CustomerID,Day_of_Week,hour_start,hour_end)
VALUES (1,1,0,'08:00','12:00'),
(1,1,0,'13:00','17:00'),
(1,2,0,'17:00','23:59')
;WITH DaysofWeek AS
(
SELECT 0 AS dow
UNION ALL
SELECT dow+1
FROM DaysofWeek
WHERE dow<5
)
SELECT *
FROM #Customer C
CROSS APPLY #Rest R
CROSS APPLY DaysofWeek D
LEFT JOIN #Restrictions X
ON X.Day_of_Week=D.dow
AND X.CustomerID=C.Id
AND X.RestID=R.Id
ORDER BY C.Id, D.dow, R.Id
I have three tables (Oracle):
sales_order
-------------
int so_key (pk)
int part_key (fk)
int condition_key (fk)
number unit_price
int qty_ordered
number unit_cost
date entry_date
quote
-------------
int q_key (pk)
int part_key (fk)
int condition_key (fk)
number unit_price
int qty_quoted
date entry_date
stock
-------------
int stock_key (pk)
int part_key (fk)
int condition (fk)
int qty_available
number unit_cost
And all three have foreign key references to these two tables:
part
-------------
int part_key (pk)
condition
-------------
int condition_key (pk)
I am writing a query that will aggregate the data into rows grouped by part and condition. However, I am unable to figure out how to group by BOTH part and condition. Here is the (functional) query that I have that groups by part only:
WITH
ctePart_Quotes AS
(
SELECT q.part_key
, COUNT(*) AS quotes_count
, SUM(q.unit_price * q.qty_quoted) AS quotes_amt_total
, SUM(q.qty_quoted) AS quotes_qty_total
FROM quote q
WHERE q.entry_date BETWEEN TO_DATE('01-Jan-2011', 'dd-mm-yyyy') AND TO_DATE('31-Dec-2011', 'dd-mm-yyyy')
GROUP BY q.part_key
)
, ctePart_Sales AS
(
SELECT so.part_key
, COUNT(*) AS sales_count
, SUM(so.unit_price * so.qty_ordered) AS sales_amt_total
, SUM(so.qty_ordered) AS sales_qty_total
, SUM(so.qty_ordered * so.unit_cost) AS cost_total
FROM sales_order so
WHERE so.entry_date BETWEEN TO_DATE('01-Jan-2011', 'dd-mm-yyyy') AND TO_DATE('31-Dec-2011', 'dd-mm-yyyy')
GROUP BY so.part_key
)
, ctePart_Stock AS
(
SELECT stm.part_key
, SUM(stm.qty_available) AS total_available
, SUM(stm.qty_available * stm.unit_cost) AS inv_cost
FROM stock stm
GROUP BY stm.part_key
)
SELECT p.part_key,
part_stock.total_available,
part_stock.inv_cost,
sales.sales_amt_total,
sales.sales_qty_total,
sales.sales_count,
sales.cost_total,
quotes.quotes_amt_total,
quotes.quotes_qty_total,
quotes.quotes_count
FROM parts p
LEFT OUTER JOIN ctePart_Quotes quotes
ON quotes.part_key = p.part_key
LEFT OUTER JOIN ctePart_Sales sales
ON sales.part_key = p.part_key
LEFT OUTER JOIN ctePart_Stock part_stock
ON part_stock.part_key = p.part_key
WHERE NOT(sales_amt_total IS NULL
AND sales_qty_total IS NULL
AND sales_count IS NULL
AND cost_total IS NULL
AND quotes_amt_total IS NULL
AND quotes_qty_total IS NULL
AND quotes_count IS NULL)
AND SALES_AMT_TOTAL > 10000
This query produces this output (totals grouped by part_key):
part_key | total_available | inv_cost | sales_amt_total | ...
---------|-----------------|----------|-----------------| ...
234 | 59 | 4923.90 | 29403.48 | ...
185 | 21 | 192.64 | 9034.95 | ...
102 | 102 | 8738.34 | 50382.20 | ...
...
But I'm trying to modify the query to produce this (totals grouped by part_key and condition_key):
part_key | condition_key | total_available | inv_cost | sales_amt_total | ...
---------|---------------|-----------------|----------|-----------------| ...
234 | 3 | 24 | 2360.50 | 16947.18 | ...
234 | 7 | 35 | 2563.40 | 12456.30 | ...
...
How do you do this?
EDIT: for clarification:
The complexity lies in: how do you join the condition in the final select? Because you are selecting FROM part but the condition relationship is through the other tables (sales_order, etc.). So you'd have to join through each of the tables (LEFT OUTER JOIN condition cond ON quotes.condition_key = cond.condition_key, etc.) but those joins would each be separate columns.
EDIT #2: someone provided a good image of the data model that illustrates the (proper, legitimate) relationship between part/condition but also the subtle complexity faced in this problem:
The main problem here seems to be your data model. Converting the table "descriptions" of your question into DDL code, and reversing this to a relational model (using Oracle Datamodeler), we find something like this:
DDL code
create table part ( part_key number primary key ) ;
create table condition ( condition_key number primary key ) ;
create table sales_order (
so_key number generated always as identity start with 3000 primary key
, part_key number references part
, condition_key number references condition
, unit_price number
, qty_ordered number
, unit_cost number
, entry_date date ) ;
create table quote (
q_key number generated always as identity start with 4000 primary key
, part_key number references part
, condition_key number references condition
, qty_quoted number
, unit_price number
, entry_date date );
create table stock (
stock_key number generated always as identity start with 5000 primary key
, part_key number references part
, condition_key number references condition
, qty_available number
, unit_cost number ) ;
Relational model (Oracle SQL Developer Data Modeler)
Looking at the model, it becomes clear that each PART can have several CONDITIONs. Thus, it may be necessary (for you) to decide, which condition you are referring to. That may not be easy. Suppose we have a part (with part_key) 1000. Now, we can record 3 different conditions, and use a specific condition for each of your 3 tables mentioned in your query.
-- one part, 3 conditions
begin
insert into part ( part_key ) values ( 1000 ) ;
insert into condition( condition_key ) values ( 2001 ) ;
insert into condition( condition_key ) values ( 2002 ) ;
insert into condition( condition_key ) values ( 2003 ) ;
insert into sales_order ( part_key, condition_key ) values ( 1000, 2001 ) ;
insert into quote ( part_key, condition_key ) values ( 1000, 2002 ) ;
insert into stock ( part_key, condition_key ) values ( 1000, 2003 ) ;
end ;
/
Which one of the 3 condition is supposed to be used for the query? Hard to tell.
-- not using WITH (subquery factoring) here - for clarity
select
P.part_key
, SO.condition_key
, Q.condition_key
, ST.condition_key
from part P
join sales_order SO on SO.part_key = P.part_key
join quote Q on Q.part_key = P.part_key
join stock ST on ST.part_key = P.part_key
;
-- output
PART_KEY CONDITION_KEY CONDITION_KEY CONDITION_KEY
1000 2001 2002 2003
Well - we could pick one of the conditions, couldn't we. However, even more conditions can exist for one and the same part ...
begin
insert into condition( condition_key ) values ( 2004 ) ;
insert into condition( condition_key ) values ( 2005 ) ;
insert into condition( condition_key ) values ( 2006 ) ;
insert into sales_order ( part_key, condition_key ) values ( 1000, 2004 ) ;
insert into quote ( part_key, condition_key ) values ( 1000, 2005 ) ;
insert into stock ( part_key, condition_key ) values ( 1000, 2006 ) ;
end ;
/
-- Same query as above now gives us:
PART_KEY CONDITION_KEY CONDITION_KEY CONDITION_KEY
1000 2001 2005 2006
1000 2001 2005 2003
1000 2001 2002 2006
1000 2001 2002 2003
1000 2004 2005 2006
1000 2004 2005 2003
1000 2004 2002 2006
1000 2004 2002 2003
Conclusion: Fix your data model. (We know this is sometimes easier said than done ...) Then, it will make sense to do some more work on your query.
__Update__
Now that we know that nothing can be done about the tables and the constraints, maybe the following queries will give you a starting point. We do not have proper test data, so let's just add some random values to the tables ...
-- PART and CONDITION -> 1000 integers each
begin
for i in 1 .. 1000
loop
insert into part ( part_key ) values ( i ) ;
insert into condition( condition_key ) values ( i ) ;
end loop;
end ;
/
Table QUOTE
-- 2 12s, 2 18s
SQL> select * from quote ;
Q_KEY PART_KEY CONDITION_KEY QTY_QUOTED UNIT_PRICE ENTRY_DATE
4000 10 100 55 500 01-MAY-11
4001 12 120 55 500 01-MAY-11
4002 12 37 56 501 01-MAY-11
4003 14 140 55 500 01-MAY-11
4004 15 46 56 501 01-MAY-11
4005 16 160 55 500 01-MAY-11
4006 18 180 55 500 01-MAY-11
4007 18 55 56 501 01-MAY-11
4008 20 200 55 500 01-MAY-11
Table SALES_ORDER
SQL> select * from sales_order ;
SO_KEY PART_KEY CONDITION_KEY UNIT_PRICE QTY_ORDERED UNIT_COST ENTRY_DATE
3000 10 100 500 55 400 05-MAY-11
3001 12 120 500 55 400 05-MAY-11
3002 14 140 500 55 400 05-MAY-11
3003 16 160 500 55 400 05-MAY-11
3004 18 180 500 55 400 05-MAY-11
3005 20 200 500 55 400 05-MAY-11
Table STOCK
SQL> select * from stock ;
STOCK_KEY PART_KEY CONDITION_KEY QTY_AVAILABLE UNIT_COST
5000 10 100 10 400
5001 12 120 10 400
5002 14 140 10 400
5003 14 100 12 402
5004 16 160 10 400
5005 18 180 10 400
5006 20 200 10 400
Assuming that only valid part/condition combinations are recorded, we can use FULL OUTER JOINs to get a first picture.
SQL> select
2 Q.part_key q_part , Q.condition_key q_cond
3 , SO.part_key so_part, SO.condition_key so_cond
4 , ST.part_key st_part, ST.condition_key st_cond
5 from quote Q
6 full join sales_order SO
7 on SO.part_key = Q.part_key and SO.condition_key = Q.condition_key
8 full join stock ST
9 on ST.part_key = SO.part_key and ST.condition_key = SO.condition_key
10 ;
-- result
Q_PART Q_COND SO_PART SO_COND ST_PART ST_COND
10 100 10 100 10 100
12 120 12 120 12 120
12 37 NULL NULL NULL NULL
14 140 14 140 14 140
15 46 NULL NULL NULL NULL
16 160 16 160 16 160
18 180 18 180 18 180
18 55 NULL NULL NULL NULL
20 200 20 200 20 200
NULL NULL NULL NULL 14 100
Then, we can use Analytic Functions for the various calculations. Note that we do not use GROUP BY here, the grouping is done via ... partition by Q.part_key, Q.condition_key ... (More about analytic functions: Oracle documentation, and examples here).
-- Skeleton query ...
-- Note that you will have need to write over(...) several times.
-- Add a WHERE clause and conditions as required.
select
Q.part_key as q_part, Q.condition_key as q_cond,
count( Q.part_key ) over ( partition by Q.part_key, Q.condition_key ) as q_count
--
-- Q example sums
-- , sum( Q.unit_price * Q.qty_quoted )
-- over ( partition by Q.part_key, Q.condition_key ) as qat -- quotes_amt_total
-- , sum( Q.qty_quoted )
-- over ( partition by Q.part_key, Q.condition_key ) as qqt -- quotes_qty_total
--
, SO.part_key as so_part, SO.condition_key as so_cond
, count( SO.part_key ) over ( partition by SO.part_key, SO.condition_key ) as so_count
--
-- SO sums here
--
, ST.part_key as st_part, ST.condition_key as st_cond
, count( ST.part_key ) over ( partition by ST.part_key, ST.condition_key ) as st_count
from sales_order SO
full join quote Q
on SO.part_key = Q.part_key and SO.condition_key = Q.condition_key
full join stock ST
on ST.part_key = SO.part_key and ST.condition_key = SO.condition_key
-- where ...
;
Result
-- output
Q_PART Q_COND Q_COUNT SO_PART SO_COND SO_COUNT ST_PART ST_COND ST_COUNT
10 100 1 10 100 1 10 100 1
12 37 1 NULL NULL 0 NULL NULL 0
12 120 1 12 120 1 12 120 1
14 140 1 14 140 1 14 140 1
15 46 1 NULL NULL 0 NULL NULL 0
16 160 1 16 160 1 16 160 1
18 55 1 NULL NULL 0 NULL NULL 0
18 180 1 18 180 1 18 180 1
20 200 1 20 200 1 20 200 1
NULL NULL 0 NULL NULL 0 14 100 1
The trick is to first create a Carthesian product (The condition table only has ~30 rows), and maybe suppress the unwanted result rows later:
This may look sub-optimal, but it will avoid a join onCOALESCE()d keyfields, which could perform badly.
WITH
ctePart_Quotes AS
(
SELECT q.part_key, q.condition_key
, COUNT(*) AS quotes_count
, SUM(q.unit_price * q.qty_quoted) AS quotes_amt_total
, SUM(q.qty_quoted) AS quotes_qty_total
FROM quote q
WHERE q.entry_date BETWEEN TO_DATE('01-Jan-2011', 'dd-mm-yyyy') AND TO_DATE('31-Dec-2011', 'dd-mm-yyyy')
GROUP BY q.part_key, q.condition_key
)
, ctePart_Sales AS
(
SELECT so.part_key, so.condition_key
, COUNT(*) AS sales_count
, SUM(so.unit_price * so.qty_ordered) AS sales_amt_total
, SUM(so.qty_ordered) AS sales_qty_total
, SUM(so.qty_ordered * so.unit_cost) AS cost_total
FROM sales_order so
WHERE so.entry_date BETWEEN TO_DATE('01-Jan-2011', 'dd-mm-yyyy') AND TO_DATE('31-Dec-2011', 'dd-mm-yyyy')
GROUP BY so.part_key, so.condition_key
)
, ctePart_Stock AS
(
SELECT stm.part_key, stm.condition_key
, SUM(stm.qty_available) AS total_available
, SUM(stm.qty_available * stm.unit_cost) AS inv_cost
FROM stock stm
GROUP BY stm.part_key, stm.condition_key
)
SELECT p.part_key,
c.condition_key,
part_stock.total_available,
part_stock.inv_cost,
sales.sales_amt_total,
sales.sales_qty_total,
sales.sales_count,
sales.cost_total,
quotes.quotes_amt_total,
quotes.quotes_qty_total,
quotes.quotes_count
FROM parts p
CROSS JOIN condition c -- <<-- Here
LEFT OUTER JOIN ctePart_Quotes quotes
ON quotes.part_key = p.part_key
AND quotes.condition_key = c.condition_key -- <<-- Here
LEFT OUTER JOIN ctePart_Sales sales
ON sales.part_key = p.part_key
AND sales.condition_key = c.condition_key -- <<-- Here
LEFT OUTER JOIN ctePart_Stock part_stock
ON part_stock.part_key = p.part_key
AND part_stock.condition_key = c.condition_key -- <<-- Here
WHERE NOT(sales_amt_total IS NULL
AND sales_qty_total IS NULL
AND sales_count IS NULL
AND cost_total IS NULL
AND quotes_amt_total IS NULL
AND quotes_qty_total IS NULL
AND quotes_count IS NULL) -- <<-- And maybe Here, too
AND SALES_AMT_TOTAL > 10000
;
Maybe this works for you:
WITH
ctePart_Quotes AS
(
SELECT q.part_key,
q.condition_key
, COUNT(*) AS quotes_count
, SUM(q.unit_price * q.qty_quoted) AS quotes_amt_total
, SUM(q.qty_quoted) AS quotes_qty_total
FROM quote q
WHERE q.entry_date BETWEEN TO_DATE('01-Jan-2011', 'dd-mm-yyyy') AND TO_DATE('31-Dec-2011', 'dd-mm-yyyy')
GROUP BY q.part_key,
q.condition_key
)
, ctePart_Sales AS
(
SELECT so.part_key,
so.condition_key
, COUNT(*) AS sales_count
, SUM(so.unit_price * so.qty_ordered) AS sales_amt_total
, SUM(so.qty_ordered) AS sales_qty_total
, SUM(so.qty_ordered * so.unit_cost) AS cost_total
FROM sales_order so
WHERE so.entry_date BETWEEN TO_DATE('01-Jan-2011', 'dd-mm-yyyy') AND TO_DATE('31-Dec-2011', 'dd-mm-yyyy')
GROUP BY so.part_key,
so.condition_key
)
, ctePart_Stock AS
(
SELECT stm.part_key,
stm.condition_key
, SUM(stm.qty_available) AS total_available
, SUM(stm.qty_available * stm.unit_cost) AS inv_cost
FROM stock stm
GROUP BY stm.part_key,
stm.condition_key
)
SELECT p.part_key,
cte.condition_key,
cte.total_available,
cte.inv_cost,
cte.sales_amt_total,
cte.sales_qty_total,
cte.sales_count,
cte.cost_total,
cte.quotes_amt_total,
cte.quotes_qty_total,
cte.quotes_count
FROM parts p
LEFT OUTER JOIN (SELECT coalesce(quotes.part_key, sales.part_key, part_stock.part_key) part_key,
coalesce(quotes.condition_key, sales.condition_key, part_stock.condition_key) condition_key,
quotes.quotes_count,
quotes.quotes_amt_total,
quotes.quotes_qty_total,
sales.sales_count,
sales.sales_amt_total,
sales.sales_qty_total,
sales.cost_total,
part_stock.total_available,
part_stock.inv_cost
FROM ctePart_Quotes quotes
FULL JOIN ctePart_Sales sales
ON sales.part_key = quotes.part_key
AND sales.condition_key = quotes.condition_key
FULL JOIN ctePart_Stock part_stock
ON part_stock.part_key = sales.part_key
AND part_stock.condition_key = sales.condition_key) cte
ON cte.part_key = p.part_key
WHERE NOT(sales_amt_total IS NULL
AND cte.sales_qty_total IS NULL
AND cte.sales_count IS NULL
AND cte.cost_total IS NULL
AND cte.quotes_amt_total IS NULL
AND cte.quotes_qty_total IS NULL
AND cte.quotes_count IS NULL)
AND SALES_AMT_TOTAL > 10000;
It also groups by condition_key in the CTEs. Then it FULL JOINs the CTEs together using coalesce to compensate for null values of part_key or condition_key in the first tables (But maybe there is none, if every combination of part_keyand condition_key, that is present in one of the tables is also present in the respective two other tables.). The result is then LEFT JOINed to part using part_key.
i have three tables orders, orders_delivered, orders_delivered_sta
and the data in the three tables look like
table orders
orders_id
10
11
12
13
table orders_delivered
orders_delivered_id orders_id
10 1000
10 1001
11 1002
12 1003
12 1004
13 1005
13 1006
13 1007
table orders_delivered_sta
orders_delivered_sta_id orders_delivered_id date now_ind
1 1000 02/11/2011 0
2 1000 01/10/2006 0
3 1000 09/13/2011 0
4 1001 01/19/2010 0
5 1001 02/21/2011 0
6 1002 02/11/2009 0
7 1002 08/27/2010 0
8 1003 07/15/2012 0
9 1004 03/09/2007 0
10 1010 10/01/2010 0
11 1011 03/27/2011 0
12 1012 07/25/2010 0
13 1013 09/18/2004 0
so i need to update orders_delivered_sta table such that now_ind should be 1 for the max date of one orders_delivered_id
like for one orders_delivered_id 1000 the max date is 09/13/2011 for this set of orders_delivered_id and date (1000,09/13/2011) the now_ind should be 1 and if the column orders_delivered_id has one and only one id then that should be changed to 1
there is some data in orders_delivered_sta table which are not in orders and orders_delivered tables those need not to be changed. the orders_delivered_id which are in oreders_delivered table only needs to change
so the desired output should look like
table orders_delivered_sta
orders_delivered_sta_id orders_delivered_id date now_ind
1 1000 02/11/2011 0
2 1000 01/10/2006 0
3 1000 09/13/2011 1
4 1001 01/19/2010 0
5 1001 02/21/2011 1
6 1002 02/11/2009 0
7 1002 08/27/2010 1
8 1003 07/15/2012 1
9 1004 03/09/2007 1
10 1010 10/01/2010 0
11 1011 03/27/2011 0
12 1012 07/25/2010 0
13 1013 09/18/2004 0
table structure:
create table orders
(
order_id int primary key
)
insert into orders select 10
insert into orders select 11
insert into orders select 12
insert into orders select 13
create table orders_delivered
(
orders_delivered_id int primary key,
orders_id int FOREIGN KEY(orders_id)REFERENCES orders (orders_id)
)
insert into orders_delivered select 1000,10
insert into orders_delivered select 1001,10
insert into orders_delivered select 1002,11
insert into orders_delivered select 1003,12
insert into orders_delivered select 1004,12
insert into orders_delivered select 1005,13
insert into orders_delivered select 1006,13
insert into orders_delivered select 1007,13
create table orders_delivered_sta
(
orders_delivered_sta_id int primary key,
orders_delivered_id int FOREIGN KEY(orders_delivered_id)REFERENCES orders_delivered (orders_delivered_id),
date char(10),
now_ind int
)
insert into orders_delivered_sta select 1,1000,'02/11/2011', 0
insert into orders_delivered_sta select 2,1000,'01/10/2006', 0
insert into orders_delivered_sta select 3,1000,'09/13/2011', 0
insert into orders_delivered_sta select 4,1001,'01/19/2010', 0
insert into orders_delivered_sta select 5,1001,'02/21/2011', 0
insert into orders_delivered_sta select 6,1002,'02/11/2009', 0
insert into orders_delivered_sta select 7,1002,'08/27/2010', 0
insert into orders_delivered_sta select 8,1003,'07/15/2012', 0
insert into orders_delivered_sta select 9,1004,'03/09/2007', 0
insert into orders_delivered_sta select 10,1010,'10/01/2010', 0
insert into orders_delivered_sta select 11,1011,'03/27/2011', 0
insert into orders_delivered_sta select 12,1012,'07/25/2010', 0
insert into orders_delivered_sta select 13,1013,'09/18/2004', 0
You could use a CTE and a window MAX():
;
WITH max_dates AS (
SELECT
*,
max_date = MAX(date) OVER (PARTITION BY orders_delivered_id)
FROM orders_delivered_sta
WHERE orders_delivered_id IN (SELECT orders_delivered_id FROM orders_delivered)
)
UPDATE max_dates
SET now_ind = 1
WHERE date = max_date
References:
WITH common_table_expression (Transact-SQL)
OVER Clause (Transact-SQL)
This is the query in MySQL, but translating it to SQL-Server should be straight forward as I am using plain SQL. Notice I have changed the date to be in a different form (YYYY-MM-DD) to avoid castings from string to date.
update t3
set t3.now_ind = 1
where t3.orders_delivered_sta_id in (
select distinct t1.orders_delivered_sta_id from t1
left join (
select t2.orders_delivered_id, max(t2.adate) as MaxDate from t2
group by t2.orders_delivered_id
) t2 on (t1.orders_delivered_id = t2.orders_delivered_id) and (t1.adate = t2.MaxDate)
where t2.orders_delivered_id is not null
) and exists (
select * from o1
join od1 on (o1.order_id = od1.orders_delivered_id)
where (t3.orders_delivered_id = od1.orders_id)
)
Here is an example
Hope this helps
PS: You did need those 3 tables... I'll read questions better next time :)
Try this:
UPDATE orders_delivered_sta
SET now_ind = 1
WHERE orders_delivered_sta_id IN(
SELECT orders_delivered_sta_id
FROM (
SELECT orders_delivered_sta_id,
ROW_NUMBER() OVER(PARTITION BY orders_delivered_id ORDER BY date DESC) AS num
FROM orders_delivered_sta) AS T
WHERE T.num = 1)
I have a SQL query which pulls unit sales by item, by week:
SELECT sls_vendor,
sls_item,
sls_units,
DATEPART(week, sls_week) AS sls_date
FROM mytable
Assume I'm looking at a 8 week period, but not every item/vendor combination has a full 8 weeks of sales. However I need my query to show a null value in that instance. So the query would return 8 rows for each item/vendor combination regardless of existence.
I tried creating a temp table which has the numbers 28 to 35 and performing a left join on the query above, but that doesn't show null values. The results are no different than running the original query alone.
I can think of how this would be done using a crosstab/pivot query, but isn't this something the join should be doing?
Edit: Updated to show my join query. Datetable just has 8 rows with 1 incremental number for each calendar week.
SELECT * FROM #datetable
LEFT JOIN
(SELECT
sls_vendor,
sls_item,
sls_units,
datepart(week,sls_week) AS sls_date
FROM mytable) AS QRY
ON temp_week = qry.sls_date
Your method should work just fine:
;with mytable as (
select 1 as sls_vendor, 'Test' as sls_item, 30 as sls_units, '8/7/2011' as sls_week union
select 1 as sls_vendor, 'Test' as sls_item, 30 as sls_units, '8/14/2011' as sls_week union
select 1 as sls_vendor, 'Test' as sls_item, 30 as sls_units, '8/21/2011' as sls_week
)
,datetable as (
select 28 as temp_week union
select 29 union
select 30 union
select 31 union
select 32 union
select 33 union
select 34 union
select 35
)
SELECT * FROM datetable
LEFT JOIN
(SELECT
sls_vendor,
sls_item,
sls_units,
datepart(week,sls_week) AS sls_date
FROM mytable) AS QRY
ON temp_week=qry.sls_date
Output:
temp_week sls_vendor sls_item sls_units sls_date
28 NULL NULL NULL NULL
29 NULL NULL NULL NULL
30 NULL NULL NULL NULL
31 NULL NULL NULL NULL
32 NULL NULL NULL NULL
33 1 Test 30 33
34 1 Test 30 34
35 1 Test 30 35
Edit: If you want to include all week values for every sales vendor, include a cross join with the distinct selection of vendors:
;with mytable as (
select 1 as sls_vendor, 'Test' as sls_item, 30 as sls_units, '8/7/2011' as sls_week union
select 2 as sls_vendor, 'Test' as sls_item, 30 as sls_units, '8/14/2011' as sls_week union
select 3 as sls_vendor, 'Test' as sls_item, 30 as sls_units, '8/21/2011' as sls_week
)
,datetable as (
select 28 as temp_week union
select 29 union
select 30 union
select 31 union
select 32 union
select 33 union
select 34 union
select 35
)
SELECT * FROM datetable
cross join (select distinct sls_vendor from mytable) v
LEFT JOIN
(SELECT
sls_vendor,
sls_item,
sls_units,
datepart(week,sls_week) AS sls_date
FROM mytable) AS QRY
ON temp_week=qry.sls_date and v.sls_vendor=qry.sls_vendor
Output:
temp_week sls_vendor sls_vendor sls_item sls_units sls_date
28 1 NULL NULL NULL NULL
29 1 NULL NULL NULL NULL
30 1 NULL NULL NULL NULL
31 1 NULL NULL NULL NULL
32 1 NULL NULL NULL NULL
33 1 1 Test 30 33
34 1 NULL NULL NULL NULL
35 1 NULL NULL NULL NULL
28 2 NULL NULL NULL NULL
29 2 NULL NULL NULL NULL
30 2 NULL NULL NULL NULL
31 2 NULL NULL NULL NULL
32 2 NULL NULL NULL NULL
33 2 NULL NULL NULL NULL
34 2 2 Test 30 34
35 2 NULL NULL NULL NULL
28 3 NULL NULL NULL NULL
29 3 NULL NULL NULL NULL
30 3 NULL NULL NULL NULL
31 3 NULL NULL NULL NULL
32 3 NULL NULL NULL NULL
33 3 NULL NULL NULL NULL
34 3 NULL NULL NULL NULL
35 3 3 Test 30 35
Does it work for you?
SELECT sls_vendor,
sls_item,
sls_units,
DATEPART(WEEK, sls_week) AS sls_date
FROM (
SELECT VALUE = 28 UNION ALL
SELECT VALUE = 29 UNION ALL
SELECT VALUE = 30 UNION ALL
SELECT VALUE = 31 UNION ALL
SELECT VALUE = 32 UNION ALL
SELECT VALUE = 33 UNION ALL
SELECT VALUE = 34 UNION ALL
SELECT VALUE = 35
) dates
LEFT JOIN mytable m
ON dates.value = DATEPART(WEEK, m.sls_week)
The following query works in Data.StackExchange. See here. It gets the top Post per week by score.
WITH weeksyears
AS (SELECT w.NUMBER AS week,
y.NUMBER AS year
FROM (SELECT v.NUMBER
FROM MASTER..spt_values v
WHERE TYPE = 'P'
AND v.NUMBER BETWEEN 1 AND 52) w,
(SELECT v.NUMBER
FROM MASTER..spt_values v
WHERE TYPE = 'P'
AND v.NUMBER BETWEEN 2008 AND 2012) y),
topPostPerWeek
AS (SELECT score,
Datepart(week, creationdate) week,
Datepart(YEAR, creationdate) YEAR,
Row_number() OVER (PARTITION BY Datepart(wk, creationdate),
Datepart(
YEAR,
creationdate) ORDER BY score DESC) row
FROM posts)
SELECT *
FROM weeksyears wy
LEFT JOIN topPostPerWeekt
ON wy.week = t.week
AND wy.YEAR = t.YEAR
WHERE row = 1
OR row IS NULL
ORDER BY wy.YEAR, wy.WEEK
Every row prior to the 38 week in 2008 is empty except for week and year. As well as the rows after the 35 week in 2011.
However if you edit the query and remove OR row IS NULL the query will act just as if it were an INNER JOIN
My guess is that there's somthing in your WHERE that's referring to the "RIGHT" table. Just add OR [rightTable.field] IS NULL and you'll be fine.