Replace nulls in table B with values in table A using SQL - sql

I have a table like this:
price_families (Table A):
ID UPC
1 123
1 456
2 789
2 111
1 121
And a second table:
sales_volume (Table B):
UPC sales volume
123 13.99 2.99
456 null null
121 14.99 1.99
789 31.88 22.99
111 null null
121 null null
What I want is to replace the null values in Table B with the sales/volume values of a different UPC of the same product family (using Table A to determine price families, joining on UPC) and to order by sales desc, volume desc (for each price family).
What is the optimal way to do this? Can I use coalesce() here, or perhaps a case statement?
My output should be this:
output (Table C):
UPC sales volume
123 13.99 2.99
456 13.99 2.99
121 14.99 1.99
789 31.88 22.99
111 31.88 22.99
121 13.99 2.99

Hmmm . . . One method uses tuples for the assignment:
update b
set (sales, volume) = (select b2.sales, b2.volume
from b b2 join
a
on b2.upc = a.upc
where b2.sales is not null and b2.volume is not null
order by b2.sales desc, b2.volume desc
limit 1
)
where sales is null and volume is null;
EDIT:
If you just want the select query:
select b.upc,
coalesce(b.sales, b2.sales) as sales,
coalesce(b.volume, b2.volume) as volume
from b left join lateral
(select b2.sales, b2.volume
from b b2 join
a
on b2.upc = a.upc
where b2.sales is not null and b2.volume is not null
order by b2.sales desc, b2.volume desc
limit 1
) b2
on 1=1;

Related

Netezza SQL Join dataset A to dataset B but pull fields from B when b_date > a_date

I have 2 datasets from 2 different sources but many of the members are the same in both datasets. My select statement is :
Select a.member_id, a.start_date, a.customer_id, a.region_id, b.b_start_date, b.customer_id, b.region_id
from dataset1 a
left join dataset2 b
on a.member_id=b.member_id
I want to somehow pick up all recs in A and recs in B where a.member_id = b.member_id but bring in the fields from A when a.start_date = b.b_start_date or a.start_date > b.b_start_date and bring in the fields from B when b.b_start_date > a.start_date.
Here's a pretty small example:
Dataset A:
member_id
start_date
customer_id
region_id
1111
1/30/2021
123
555
2222
1/30/2021
222
555
3333
1/1/2021
345
678
Dataset B:
member_id
b_start_date
customer_id
region_id
1111
1/1/2022
567
444
2222
1/30/2021
222
555
Result:
member_id
customer_id
region_id
1111
567
444
2222
222
555
3333
345
678
/* try this */
select a.* from a inner join b using (member_id) where a.start_date >= b.b_start_date
union all
select b.* from a inner join b using (member_id) where b.b_start_date > a.start_date;

How to transpose rows into columns and it will become only one row?

I have this query:
SELECT DISTINCT
MAX(D.RECEIVEDDATE) AS DATE,
A.LOCATIONNUMBER AS ID_NAME,
(SELECT CARRIERNUMBER FROM CARRIER C WHERE C.CARRIERID = D.CARRIERID) AS CR_NAME,
D.DEPOSITREFERENCE As ITEM_REF,
DC.CONTAINERNUMBER As ITEM_NUM,
Z.DENOMINATION As Category,
D.RECEIVEDCONTAINERS as ITEM_QTY,
SUM(CASE WHEN Z.DENOMINATION LIKE '%50%' THEN CAST(Z.VERIFIEDQUANTITY AS BIGINT) END) QTY_50_PCS,
SUM(CASE WHEN Z.DENOMINATION LIKE '%100%' THEN CAST(Z.VERIFIEDQUANTITY AS BIGINT) END) QTY_100_PCS,
SUM(CASE WHEN Z.DENOMINATION LIKE '%50%' THEN CAST(Z.VERIFIEDAMOUNT AS BIGINT) END) AMT_50_PCS,
SUM(CASE WHEN Z.DENOMINATION LIKE '%100%' THEN CAST(Z.VERIFIEDAMOUNT AS BIGINT) END) AMT_100_PCS,
CAST(D.VERIFIEDAMOUNT AS BIGINT) AS TOTAL_AMT
FROM
DEPOSIT D
JOIN
ACCOUNT A ON D.ACCOUNTID = A.ACCOUNTID
LEFT JOIN
DEPOSITCONTAINER DC ON DC.DEPOSITID = D.DEPOSITID
LEFT JOIN
DEPOSITCONTAINERITEM Z ON Z.DEPOSITCONTAINERID = DC.DEPOSITCONTAINERID
LEFT JOIN
DEPOSITCONTAINERITEMUNIT X ON X.DEPOSITCONTAINERITEMID = Z.DEPOSITCONTAINERITEMID
WHERE
D.DEPOSITTYPE = 0
AND LOCATIONNUMBER LIKE 'Z%'
AND D.RECEIVEDDATE IS NOT NULL
AND Z.DENOMINATION IS NOT NULL
GROUP BY
D.RECEIVEDDATE, A.LOCATIONNUMBER, D.CARRIERID, D.DEPOSITREFERENCE,
D.RECEIVEDCONTAINERS, D.VERIFIEDAMOUNT, DC.CONTAINERNUMBER, DC.VERIFIEDAMOUNT, Z.DENOMINATION
ORDER BY
D.DEPOSITREFERENCE
The output/result will become like this:
DATE
ID_NAME
CR_NAME
ITEM_REF
ITEM_NUM
CATEGORY
ITEM_QTY
QTY_50_PCS
QTY_100_PCS
AMT_50_PCS
AMT_100_PCS
TOTAL_AMT
2021-07-26
XXN8A
ABC
0039546898202ZZN8A
N5-050-210001
50000
6
2000
NULL
100000000
NULL
672600000
2021-07-26
XXN8A
ABC
0039546898202ZZN8A
N5-050-210002
50000
6
2095
NULL
104750000
NULL
672600000
2021-07-26
XXN8A
ABC
0039546898202ZZN8A
N5-100-2100002
100000
6
NULL
2120
NULL
212000000
672600000
2021-07-26
XXN8A
ABC
0039546898202ZZN8A
N5-100-210001
100000
6
NULL
2069
NULL
206900000
672600000
2021-07-26
XXN8A
ABC
0039546898202ZZN8A
N5-RJC-210001
100000
6
NULL
383
NULL
38300000
672600000
2021-07-26
XXN8A
ABC
0039546898202ZZN8A
N5-RJC-210001
50000
6
213
NULL
10650000
NULL
672600000
2021-07-27
CCY57
CAB
0344416011204ZZY57
G6-50-210153
50000
6
68
NULL
3400000
NULL
140050000
2021-07-27
CCY57
CAB
0344416011204ZZY57
G6-50-210154
50000
6
75
NULL
3750000
NULL
140050000
2021-07-27
CCY57
CAB
0344416011204ZZY57
G6-RJC-210153
100000
6
NULL
486
NULL
48600000
140050000
2021-07-27
CCY57
CAB
0344416011204ZZY57
G6-RJC-210153
50000
6
26
NULL
1300000
NULL
140050000
How to get the transpose table with only one row and the columns based on the ITEM_NUM? For example, the 1st item N5-050-210001 with value quantity of 2000 will be in the QTY_50 column, then if there's second item N5-050-210002 with value 2095, it will be placed in second column QTY_50_2. This is also applied to the ITEM_NUM like '%-100-%' and '%-RJC%', the ITEM_NUM and Category columns will be eliminated and the result I want is to be like this:
DATE
ID_NAME
CR_NAME
ITEM_REF
ITEM_QTY
QTY_50
QTY_50_2
QTC_RJC_50
QTY_100
QTY_100_2
QTC_RJC_100
AMT_50_PCS
AMT_100_PCS
TOTAL_AMT
7/26/2021
XXN8A
ABC
0039546898202ZZN8A
6
2000
2095
213
2069
2120
383
215400000
457200000
672600000
7/27/2021
CCY57
CAB
0344416011204ZZY57
6
68
75
26
773
57
486
8450000
131600000
140050000
Any suggestion?
If you want a result set that has a single row for each combination of DATE, ID_NAME, CR_NAME, and ITEM_REF, then these should be the only columns in the GROUP BY. However, DATE is really an aggregation expression, so you only want the other three.
So:
SELECT MAX(RECEIVEDDATE) as DATE, ID_NAME, CR_NAME, ITEM_REF,
SUM(CASE WHEN Z.DENOMINATION LIKE '%50%' THEN CAST(Z.VERIFIEDQUANTITY AS BIGINT) END) as QTY_50_PCS,
. .
FROM . . .
GROUP BY ID_NAME, CR_NAME, ITEM_REF;

Tracing original Value through Iteration SQL

Suppose there is a data collection system that, whenever a record is altered, it is then saved as a new record with a prefix (say M-[most recent number in que and is unique]).
Suppose I am given the following data set:
Customer | Original_Val
1 1020
2 1011
3 1001
I need to find the most recent value for each customer given the following table:
Customer | Most_Recent_Val | Pretained_To_Val | date
1 M-2000 M-1050 20170225
1 M-1050 M-1035 20170205
1 M-1035 1020 20170131
1 1020 NULL 20170101
2 M-1031 1011 20170105
2 1011 NULL 20161231
3 1001 NULL 20150101
My desired output would be:
Customer | Original_Val | Most_Recent_Val | date
1 1020 M-2000 20170225
2 1011 M-1031 20170105
3 1001 1001 20150101
For customer 1, there are 4 levels i.e (M-2000 <- M-1050 <- M-1035 <- 1020) Note that there would be no more than 10 levels of depth for each customer.
Much Appreciated! Thanks in advance.
Find the min and max of each customer and then join it together. Something like this:
Select
[min].Customer
,[min].Most_Recent_Val as Original_Val
,[max].Most_Recent_Val as Most_Recent_Val
,[max].date
From
(
Select
Customer
,Most_Recent_Val
,date
From
table t1
inner join (
Select
Customer
,MIN(date) as MIN_Date
From
table
Group By
Customer
) t2 ON t2.Customer = t1.Customer
and t2.MIN_Date = t1.Date
) [min]
inner join (
Select
Customer
,Most_Recent_Val
,date
From
table t1
inner join (
Select
Customer
,MAX(date) as MAX_Date
From
table
Group By
Customer
) t2 ON t2.Customer = t1.Customer
and t2.MAX_Date = t1.Date
) [max] ON [max].Customer = [min].Customer

Counting presence of discount ranges in SQL

I have a simple orders table for a product being sold that has a column with the discount per order. For example:
Order Number DiscountPrcnt
1234 0
1235 10
1236 41
What I would like to do is create an output where I can join the order table to a customer table and group the discounts into ranges by email, as follows:
Email_Address 0-20 20-50 50-100
joe#abc.com Yes Yes
tom#abc.com Yes Yes
So the idea is to determine if each customer (here designated by email) has ever received a discount in the specified range, and if not, it should return NULL for the range.
A simplified version of the table structure is:
Customer Table:
CustID Email
123 joe#abc.com
234 tom#abc.com
456 joe#abc.com
So emails can repeat across customers.
Orders Table:
CustID OrderID Amount DiscPrcnt
123 1234 50.00 0
234 1235 75.00 10
456 1236 20.00 41
select c.email, COUNT(o1.order_number), COUNT(o2.order_number), COUNT(o3.order_number)
from customer c, order o1, order o2, order o3
where c.CustID = o1.CustId
and c.CustID = o2.CustID
and c.CustID = o3.CustID
and o1.DiscountPrcnt > 0
and o1.DiscountPrcnt <= 20
and o2.DiscountPrcnt > 2
and o2.DiscountPrcnt <= 50
and o3.DiscountPrcnt > 50
and o3.DiscountPrcnt <= 100
group by c.email
You'll not get yes as expected but the number of time the user benefits the discount. 0 if none.

SQL Group and Join

In SQL Server 2008, I have a table that looks like this:
ID | RefNum | Label | Value | Status
------------------------------------------------------
1 123 OrderNum 123456 0
2 123 TrackingNum 111111 0
3 123 ConfNum 989898 0
4 234 OrderNum 234567 1
5 234 TrackingNum 222222 1
6 234 ConfNum 878787 0
7 567 OrderNum 345678 1
8 567 TrackingNum 333333 0
9 567 ConfNum 767676 0
I want to select all records where Status = 0 and join, based on RefNum, to the 'OrderNum' and 'TrackingNum' Label values, regardless of whether 'OrderNum' and TrackingNum Statuses are 1 or 0. For example, the query should produce:
ID | RefNum | Label | Value | Status |OrderNum|TrackingNum
------------------------------------------------------------------------
1 123 OrderNum 123456 0 123456 111111
2 123 TrackingNum 111111 0 123456 111111
3 123 ConfNum 989898 0 123456 111111
6 234 ConfNum 878787 0 234567 222222
8 567 TrackingNum 333333 0 345678 333333
9 567 ConfNum 767676 0 345678 333333
Right now, my query looks like this:
SELECT Id
,mT.RefNum
,Label
,Value
,Status
,OrderNum
,TrackingNum
FROM [dbo].[myTable] AS mT
INNER JOIN (
SELECT MAX(ID) As OrderRowId, RefNum, Value AS OrderNum
FROM [dbo].[myTable]
WHERE Label= 'OrderNum'
group by RefNum, Value) AS OrderNums
ON OrderNums.RefNum= mt.RefNum
INNER JOIN (
SELECT MAX(ID) As OrderRowId, RefNum, Value AS TrackingNum
FROM [dbo].[myTable]
WHERE Label= 'TrackingNum'
group by RefNum, Value) AS TrackingNums
ON TrackingNums.RefNum= mt.RefNum
WHERE ProcessComplete = 0
This apprears to work, but requires a hash join. Would love someone to shoot holes in this or provide a more efficient solution. Thanks.
If there can't be duplicate order numbers or tracking numbers per reference number, you can simplify the query somewhat with a regular LEFT JOIN or JOIN;
SELECT mt.id, mt.refnum, mt.label, mt.value, mt.status,
ordno.value ordernum, trackno.value trackingnum
FROM myTable mt
LEFT JOIN myTable ordno
ON ordno.label='ordernum' and mt.refnum=ordno.refnum
LEFT JOIN myTable trackno
ON trackno.label='trackingnum' and mt.refnum=trackno.refnum
WHERE mt.status = 0;
An SQLfiddle to test with.
If there may be duplicates, you can still do a single GROUP BY to get a result;
SELECT mt.id, mt.refnum, mt.label, mt.value, mt.status,
MAX(ordno.value) ordernum, MAX(trackno.value) trackingnum
FROM myTable mt
LEFT JOIN myTable ordno
ON ordno.label='ordernum' and mt.refnum=ordno.refnum
LEFT JOIN myTable trackno
ON trackno.label='trackingnum' and mt.refnum=trackno.refnum
WHERE mt.status = 0
GROUP BY mt.id,mt.refnum,mt.label,mt.value,mt.status;
Another SQLfiddle.