Select all transactions that contain a specific item? - sql

Good Morning,
The question I am running into involves a database with transactions. The transactions are stored as lines by each item sold. What would the code be if I wanted to select all transactions which contained item 11222 so I can see what other items are bought with it?
I was using:
SELECT [Transaction]
, [Date]
, [Item]
, [Register]
, [Location]
WHERE [Item] = 11222
FROM "Transactions"
Then looking up each transaction with date and location separately to see, however my request is now to look at thousands of transactions. Is there a way in SQL to conditionally pull all lines, even if the specific line doesn't contain the requested item #?
Example of how the data is in the table:
Transaction Date Item Register Location
123 1/1/2019 11222 1 15
123 1/1/2019 45663 1 15
124 1/1/2019 77433 1 15
124 1/1/2019 11222 1 15
124 1/1/2019 66092 1 15
125 1/1/2019 66933 1 15
125 1/1/2019 77433 1 15
126 1/1/2019 11222 1 15
126 1/1/2019 82991 1 15
127 1/1/2019 88392 1 15
127 1/1/2019 88492 1 15

I would phrase this as exists:
SELECT t.*
FROM Transactions t
WHERE EXISTS (SELECT 1
FROM Transactions t2
WHERE t2.transaction = t.transaction AND
t2.Item = 11222
)
ORDER BY t.transaction; -- keep all the rows for a given transaction together

Related

How to get the last day of the month without LAST_DAY() or EOMONTH()?

I have a table t with:
DATE
LOCATION
PRODUCT_ID
AMOUNT
2021-10-29
1
123
10
2021-10-30
1
123
9
2021-10-31
1
123
8
2021-10-29
1
456
100
2021-10-30
1
456
90
2021-10-31
1
456
80
2021-10-29
2
123
18
2021-10-30
2
123
17
2021-11-29
2
456
18
I need to find the AMOUNT of each PRODUCT_ID for each combination of LOCATION + PRODUCT_ID.
If a PRODUCT_ID has no entry for that day the AMOUNT is NULL.
So the result should look like:
DATE
LOCATION
PRODUCT_ID
AMOUNT
2021-10-31
1
123
8
2021-10-31
1
456
80
2021-10-31
2
123
NULL
2021-11-30
2
456
NULL
Sadly EXASOL has no LAST_DAY() or EOMONTH() function. How can I solve this?
You can get to the last day of the month using a date_trunc function in combination with date_add:
case
when t.date = date_add('day', -1, date_add('month', 1, date_trunc('month', t.date)))
then 'Y' else 'N' end as end_of_month
That being said, if you group your table for all combinations of locations and products, you will not get NULLs for products without sales on the last day of the month as shown in your output table.
When you group your data, any value that does not exist will simply not show up in your output table. If you want to force nulls to show up, you can create a new table that contains all combinations of products, locations, and hard-coded end of month dates.
Then, you can left join your old table with this new hard-coded table by date, location, and product. This method will give you the NULL values you expect.

Oracle Query to find the Nth oldest visit of a person

I have the following Oracle table
PersonID
VisitedOn
1
1/1/2017
1
1/1/2018
1
1/1/2019
1
1/1/2020
1
2/1/2020
1
3/1/2020
1
5/1/2021
1
6/1/2022
2
1/1/2015
2
1/1/2017
2
1/1/2018
2
1/1/2019
2
1/1/2020
2
2/1/2020
3
1/1/2017
3
1/1/2018
3
1/1/2019
3
1/1/2020
3
2/1/2020
3
3/1/2020
3
5/1/2021
I try to write a query to return the Nth oldest visit of each person.
For instance if I want to return the 5th oldest visit (N=5) the result would be
PersonID
VisitDate
1
1/1/2020
2
1/1/2017
3
1/1/2019
I think this will work:
Ran test with this data:
create table test (PersonID number, VisitedOn date);
insert into test values(1,'01-JAN-2000');
insert into test values(1,'01-JAN-2001');
insert into test values(1,'01-JAN-2002');
insert into test values(1,'01-JAN-2003');
insert into test values(2,'01-JAN-2000');
insert into test values(2,'01-JAN-2001');
select personid, visitedon
from (
select personid,
visitedon,
row_number() over ( partition by personid order by visitedon ) rn
from test
)
where rn=5
What this does is use an analytic function to assign a row number to each set of records partitioned by the person id, then pick the Nth row from each partitioned group, where the rows in each group are sorted by date. If you run the inner query by itself, you will see where the row_number is assigned:
PERSONID VISITEDON RN
1 01-JAN-00 1
1 01-JAN-01 2
1 01-JAN-02 3
1 01-JAN-03 4
2 01-JAN-00 1
2 01-JAN-01 2

Sql query to assign value to a column having null value from other row based on different scenarios

I have the below real production data scenario and I am trying to get the desired output. I have to populate all the NULL values for the Worker from other rows (next or previous based on data).
Sample Input
PK Id Status Worker Created Date
--- --- ----------- ----------- -------------
1 101 Waiting NULL 1/1/2019 8:00
2 101 Assigned Jon Doe 1/1/2019 8:10
3 101 Initiated Jon Doe 1/1/2019 8:15
4 102 Waiting NULL 1/1/2019 8:00
5 102 Waiting NULL 1/1/2019 8:12
6 102 Assigned Jane Doe 1/1/2019 8:15
7 103 Waiting NULL 1/1/2019 8:00
9 103 Initiated Jon Doe 1/1/2019 8:15
11 103 Waiting NULL 1/1/2019 8:17
12 103 Assigned Jane Doe 1/1/2019 8:20
13 103 Assigned NULL 1/1/2019 8:22
14 103 Initiated NULL 1/1/2019 8:25
Desired Output
PK Id Status Worker Created Date
--- --- ----------- ----------- -------------
1 101 Waiting Jon Doe 1/1/2019 8:00
2 101 Assigned Jon Doe 1/1/2019 8:10
3 101 Initiated Jon Doe 1/1/2019 8:15
4 102 Waiting Jane Doe 1/1/2019 8:00
5 102 Waiting Jane Doe 1/1/2019 8:12
6 102 Assigned Jane Doe 1/1/2019 8:15
7 103 Waiting Jon Doe 1/1/2019 8:00
9 103 Initiated Jon Doe 1/1/2019 8:15
11 103 Waiting Jane Doe 1/1/2019 8:17
12 103 Assigned Jane Doe 1/1/2019 8:20
13 103 Assigned Jane Doe 1/1/2019 8:22
14 103 Initiated Jane Doe 1/1/2019 8:25
SQL:
select tl.*, RANK() OVER (ORDER BY tl.[Id],tl.[Created Date]) rnk
into #temp
from table tl
select tl.*,
case when tl.[Worker] is null t2.[Worker] else tl.[Worker] end as [Worker Updated]
from #temp tl
left join #temp t2 on tl.[Id]=t2.[Id] and tl.rnk=t2.rnk-1
I am only able to get the correct result for scenario Id 101 in the Input Data Sample. I am not sure how to handle scenario 102 (two consecutive rows having NULL on Worker column) and 103 (Last 2 rows having NULL on Worker).
Can someone please help me on this?
I think what you need is ISNULL() and MAX() OVER() so your query would have something like this :
SELECT
t1.PK
, t1.Id
, t1.Status
, ISNULL(t1.Worker, MAX(t1.Worker) OVER(PARTITION BY Id) ) Worker
, t1.CreatedDate
FROM #temp tl
ISNULL() will check the value, if is it null will replace it with the secondary value. it's the same the case that you have in your query.
MAX(t1.Worker) OVER(PARTITION BY Id)
Since the aggregation functions eliminate nulls, we take this advantage and use it with OVER() clause to partition the rows by Id and get the value that we need using one of the aggregation functions.
Possibly the simplest way is outer apply:
select t.id, t.status, t2.worker, t.date
from t outer apply
(select top (1) t2.*
from t2
where t2.worker is not null and t2.id >= t.id
order by t2.id asc
) t2;
What you really want is the IGNORE NULLS option on LEAD(). However, SQL Server does not support that.
If you want to fill in the most recent values with the preceding value, then follow the same logic with another apply:
select t.id, t.status,
coalesce(tnext.worker, tprev.worker) as worker, t.date
from t outer apply
(select top (1) t2.*
from t2
where t2.worker is not null and t2.id >= t.id
order by t2.id asc
) tnext outer apply
(select top (1) t2.*
from t2
where t2.worker is not null and t2.id <= t.id
order by t2.id desc
) tprev;

Joining to another table only on the first occurrence of a field

Note: I have tried to simplify the below to make it simpler both for me and for anyone else to understand, the tables I reference below are in fact sub-queries joining a lot of different data together from different sources)
I have a table of purchased items:
Items
ItemSaleID CustomerID ItemCode
1 100 A
2 100 B
3 100 C
4 200 A
5 200 C
I also have transaction header and detail tables coming from a till system:
TranDetail
TranDetailID TranHeaderID ItemSaleID Cost
11 51 1 $10
12 51 2 $10
13 51 3 $10
14 52 4 $20
15 52 5 $10
TranHeader
TranHeaderID CustomerID Payment Time
51 100 $100 11:00
52 200 $50 12:00
53 100 $20 13:00
I want to get to a point where I have a table like:
ItemSaleID CustomerID ItemCode Cost Payment Time
1 100 A $10 $120 11:00
2 100 B $10 11:00
3 100 C $10 11:00
4 200 D $20 $50 12:00
5 200 E $10 12:00
I have a query which produces the results but when I add in the ROW_NUMBER() case statement goes from 2 minutes to 30+ minutes.
The query is further confused because I need to supply the earliest date relating to the list of transactions and the total price paid (could be many transactions throughout the day for upgrades etc)
Query below:
SELECT ItemSaleID
, CustomerID
, ItemCode
, Cost
, CASE WHEN ROW_NUMBER() OVER (PARTITION BY TranHeaderID ORDER BY ItemSaleID) = 1
THEN TRN.Payment ELSE NULL END AS Payment
FROM Items I
OUTER APPLY (
SELECT TOP 1 SUB.Payment, Time
FROM TranHeader H
INNER JOIN TranDetail D ON H.TranHeaderID = D.TranHeaderID
OUTER APPLY (SELECT SUM(Payment) AS Payment
FROM TranHeader H2
WHERE H2.CustomerID = Items.CustomerID
) SUB
WHERE D.CustomerID = I.CustomerID
) TRN
WHERE ...
Is there a way that I can only show payments for each occurrence of the customer ID whilst maintaining performance

sql query to link two tables

I have a table called users where the employee data are stored.
Also I have another table payment_details where employee's payment related data are stored
the two tables are as follows.
this table is users
sr_no emp_no username payment
1 1001 leroy <null>
2 1003 harry <null>
3 1004 Tom <null>
4 1008 Jon <null>
This table below is payment_details
sr_no name number month status date
43 Jon 1008 January paid 5/16/2012
44 Jon 1008 January balance 5/16/2012
45 Harry 1003 January paid 5/16/2012
46 Tom 1004 January paid 5/16/2012
47 leroy 1001 January paid 5/16/2012
48 Jon 1008 January paid 5/16/2012
49 Harry 1003 January paid 5/16/2012
50 Jon 1008 February balance 5/16/2012
51 leroy 1001 February paid 5/16/2012
52 Jon 1008 February paid 5/16/2012
53 Tom 1004 February balance 5/16/2012
My question here is to update "users" table payment column to "paid" when the status of his/her is all paid in payment_details table
You can either do this: http://www.sqlfiddle.com/#!3/db13f/18
update users set payment = 'paid'
from
(
select number
from payment_details
group by number
having sum(case when status = 'paid' then 1 end)
= count(*)
) as x
where x.number = users.emp_no;
Or this: http://www.sqlfiddle.com/#!3/db13f/19
update users
set payment = x.upd
from
(
select u.emp_no,
case when sum(case when d.status = 'paid' then 1 end) = count(*) then
'paid'
else
null
end as upd
from users u
left join payment_details d
on d.number = u.emp_no
group by u.emp_no
) as x
where x.emp_no = users.emp_no;
Their difference is how many rows it updates. On the second query, it updates all users, regardless if the user has a paid all status('paid') or not(null); on the first query, it updates only those who are paid.
The advantage of the second query, is when you change one of the all 'paid' status on payment_detail of a given user to 'not paid' for example, it can revert back the user's payment status to null
UPDATE order_details
SET payment= 'paid'
WHERE not EXISTS (SELECT 1
FROM payment_details
WHERE payment_details.emp_no= order_details.emp_no
AND payment_details.status <> 'paid'
)
There also simple way to get the value of particulars table into variable and update the tabe as below:
declare #bb varchar(50)
select #bb= status from payment_details where name=#name and id=#id
update uuser set payment = #bb
where name = #name and id=#id