Use column from first table as condition for joining second table - sql

Suppose I have
products:
folio price quantity
1 100.00 1
1 450.00 2
3 150.00 1
4 600.00 2
terms:(to know how many payment terms depending on the price of the product)
level term
0.01 12
100.00 14
200.00 16
300.00 18
400.00 20
500.00 22
What can I do to have a resulting table like this:
folio price quantity term
1 100.00 1 14
1 450.00 2 20
I've tried using:
SELECT a.*, b.term
FROM products AS a
JOIN terms AS b ON b.level <= a.price
WHERE a.folio = 1
But I end up getting:
folio price quantity term
1 100.00 1 12
1 100.00 1 14
1 450.00 2 12
1 450.00 2 14
1 450.00 2 16
1 450.00 2 18
1 450.00 2 20
What can I do so I only get the row with the biggest term? Please help!

You are looking for one row from the terms table, not all of them. One way to do this is with a correlated subquery:
SELECT p.*,
(select t.term from terms t where p.price >= t.level order by t.level desc limit 1
) as term
FROM products p
WHERE p.folio = 1;
If you can modify your terms table to have a minimum and maximum price, then that would make it easier to user.
And, you can mimic this with the lead() function:
select p.*, t.term
from products p left outer join
(select t.*, lead(level) over (order by level) as nextlevel
from terms t
) t
on p.price >= t.level and (p.price < t.nextlevel or t.nextlevel is null)
where p.folio = 1;

Related

Find count of transactions and customer where they have purchased one item along with something else SQL

I have a table which has the transaction details of the customer:
Customernum sls unit txn no SKU txn_date
1 10 30 567 903633 2019-02-01 yes
1 20 30 567 123767 2019-02-01 yes
1 50 40 567 126658 2019-03-01 yes
1 10 40 345 773633 2019-02-10 yes
1 12 30 345 965322 2019-02-10
1 10 50 678 838364 2019-02-15 yes
1 10 70 975 983636 2019-02-28 yes
2 11 80 910 903633 2019-02-11
2 11 90 910 566373 2019-02-12 yes
3 11 62 855 678364 2019-02-12
I have another table which has the SKU:
sku Desc
123767 APP
903633 CD
773633 APP
838364 APP
983636 APP
566373 APP
126658 APP
I need to find the transactions from the above transactions which has 903633 in the transaction along with the other SKU from the SKU table and transactions which have more than one of the SKU' from the above table.
So the two questions I am trying to anwer is :
How many transactions included only 1 apparel item along with CD
How many transactions included more than one apparel items with CD.
I have tried the below query but I have had no luck:
select a.Customernum, a.txnno, td.sku,
case when ps.sku is not null
then 'Yes'
else 'No'
end as is_sku,
case when count(b.sku) over (partition by a.txnno) > 0
then 'Yes'
else 'No'
end as has_sku
from table a
left join sku b on b.sku = a.sku;
I am expecting the below result:
Result 1- How many transactions included only 1 apparel item along with CD
customernum txnno unit sls
2 910 80 11
2 910 90 11
1 678 50 10
1 975 70 10
Result 2- How many transactions included more than one apparel items with CD.
customernum txnno unit sls
1 567 30 10
1 567 30 20
1 567 40 50
Use two levels of aggregation. The subquery counts the number of "app"s and "cd"s. The outer filter to just one "cd" (you can use >= if you want at least one) and aggregates by the number of "app"s:
select num_app, count(*) as num_customers
from (select t.customernum,
sum(case when s.desc = 'APP' then 1 else 0 end) as num_app,
sum(case when s.desc = 'CD' then 1 else 0 end) as num_cd
from transactions t join
skus s
on t.sku = s.sku
) c
where num_cd = 1
group by num_app
order by num_app;
You can filter with window functions.
The following query would give you all transactions that involve sku 903633 with exactly one other sku:
select *
from (
select
t.*,
count(*) over(partition by txnno) cnt,
max(case when sku = 903633 then 1 end) has_cd
from mytable t
) t
where has_cd = 1 and cnt = 2
To get the transactions that involve sku 903633 and at least two other skus, you can just change cnt = 2 to cnt > 2.

sum two row with condition

I have one table as below if two rows has same item and location then two one should become one one row and give the total of qty
item location qty
a 1010 10
a 1020 5
a 1010 5
b 1010 2
b 1010 5
b 1020 1
c 1010 5
result
item location qty
a 1010 15
a 1020 5
b 1010 7
b 1020 1
c 1010 5
SELECT item, location, SUM(qty)
FROM xTable
GROUP BY item, location
SELECT t.item, t.location, SUM(t.qty)
FROM test t
GROUP BY t.item, t.location
ORDER BY t.item, t.location

How to get 2 most recent values in postgreSQL in one row?

I have this table of items in order:
orderitemid orderid itemid quantity price createdate
1 1 12 5 15.5 2016-12-04 11:35:02.06629
2 1 17 5 13.2 2016-12-04 11:32:02.06629
3 2 12 2 12.5 2016-12-05 11:35:02.06629
4 2 17 1 12.6 2016-12-05 11:35:02.06629
5 2 18 15 14.5 2016-12-04 11:35:02.06629
6 3 12 45 3 2015-12-04 11:35:02.06629
I have a query which gives the most recent order of each item so:
select distinct on (itemid) *
from orderitems
order by itemid,createdate
this gives:
orderitemid orderid itemid quantity price createdate
3 2 12 2 12.5 2016-12-05 11:35:02.06629
4 2 17 1 12.6 2016-12-05 11:35:02.06629
5 1 18 15 14.5 2016-12-04 11:35:02.06629
Now what I want is to get in the same row per item information about the previous order of the item. Basically to compare the most recent order of item with the 2nd most recent order of item
This is what I want:
orderitemid itemid quantity price 2ndquantity 2ndprice 2ndorderitemid
3 12 2 12.5 5 15.5 1
4 17 1 12.6 1 13.2 2
5 18 15 14.5
How can I modify my query to do that?
with ranked as (
select orderitemid, orderid, itemid, quantity, price, createdate,
row_number() over (partition by itemid order by createdate desc) as rn
from orderitems
)
select r1.*, r2.quantity as "2ndquantity", r2.price as "2ndprice",
r2.orderitemid as "2ndorderitemid"
from ranked r1
left join ranked r2 on r1.itemid = r2.itemid and r2.rn = 2
where r1.rn = 1;
The CTE calculates the 1st and 2nd items and the final select then brings them together with a join. Note that you need a left join as there might not be a 2nd row and in that case that item would not show up at all.
Online example: http://rextester.com/SDBZ21144
Hmmm you want to compare all orderitem rows against the next most recent price?
WITH comparison_rank AS (
SELECT orderitemid, itemid, price, quantity
rank() over (partition by itemid order by createddate)
FROM orderitems
)
SELECT o.orderitemid, o.itemid, o.price, o.quantity, o.price, o.createddate,
p.price as prevprice, p.quantity as prevqty
FROM orderitems o JOIN comparison_rank c ON o.orderitemid = c.orderitemid
LEFT JOIN comparison_rank p ON c.rank = p.rank + 1 AND c.itemid = p.itemid;
This will not perform so well over a large data set however. To improve performance I think you need to improve your data model to improve performance.

complex paratition sum in postgresql

I have tables as follow:
A deliveries
delveryid clientid deliverydate
1 10 2015-01-01
2 10 2015-02-02
3 11 2015-04-08
B items in deliveris
itemid deliveryid qty status
70 1 5 1
70 1 8 2
70 2 10 1
72 1 12 1
70 3 100 1
I need to add a column to my query that gives me the qty of each part in other deliveris of the same client.
meaning that for given data of client 10 and delivery id 1 I need to show:
itemid qty status qtyOther
70 5 1 10 //itemid 70 exists in delivery 2
70 8 2 10 //itemid 70 exists in delivery 2
72 12 1 0 //itemid 72 doesn't exists in other delivery of client 11
Since I need to add qtyOther to my existing qry i'm trying to avoid using Group By as it's a huge query and if I use SUM in select I will have to group by all items in select.
This is what I have so far:
Select ....., coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid) ,0) AS qtyOther
FROM B b
LEFT JOIN A a USING
LEFT JOIN (other tables)
WHERE clientid=10 ....
This query gives me the total sum of qty per itemid for specific clientid, regardless of which delivery it is. How do I change it so it will consider the delivryid? I need something like:
coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid) FROM B where deliveryid<>b.deliveryid ,0) AS qtyOther
Any suggestions how to do that?
Note: I can NOT change the condition in WHERE.
I think you just want to subtract out the total for the current delivery:
Select .....,
(coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid), 0) -
coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid, a.deliveryid), 0)
) as qtyOther

TSQL - divide rows into groups based on one field

This is modified version of my earlier question: TSQL equally divide resultset to groups and update them
I have my database with 2 tables like so:
Orders table has data like below:
OrderID OperatorID GroupID OrderDesc Status Cash ...
--------------------------------------------------------------------------
1 1 1 small_order 1 300
2 1 1 another_order 1 0
3 1 2 xxxxxxxxxxx 2 1000
5 2 2 yyyyyyyyyyy 2 150
9 5 1 xxxxxxxxxxx 1 50
10 NULL 2 xxxxxxxxxxx 1 150
11 NULL 3 xxxxxxxxxxx 1 -50
12 4 1 xxxxxxxxxxx 1 200
Operators table:
OperatorID Name GroupID Active
---------------------------------------
1 John 1 1
2 Kate 1 1
4 Jack 2 1
5 Will 1 0
6 Sam 3 0
I'm able to equally divide my recordset into equally groups using below query:
SELECT o.*, op.operatorName AS NewOperator, op.operatorID AS NewOperatorId
FROM (SELECT o.*, (ROW_NUMBER() over (ORDER BY newid()) % numoperators) + 1 AS randseqnum
FROM Orders o CROSS JOIN
(SELECT COUNT(*) AS numoperators FROM operators WHERE operators.active=1) op
WHERE o.status in (1,3)
) o JOIN
(SELECT op.*, ROW_NUMBER() over (ORDER BY newid()) AS seqnum
FROM Operators op WHERE op.active=1
) op
ON o.randseqnum = op.seqnum ORDER BY o.orderID
Demo available at: http://sqlfiddle.com/#!3/ff47b/1
Using script from above I can divide Orders to (almost) equal groups but based on number or Orders for Operator, but I need to modify it so that it will assign Operators to Orders based on sum or Cash for orders.
For example:
If I have 6 Orders with Cash values: 300, 0, 50, 150, -50, 200 they sum gives 650.
My script should assign to 3 Operators random 2 Orders with random sum of Cash for Orders.
What I would like to get is to assign for example 300,-50 to operator1, 200, 0 to second and 150, 50 to third.
Hope this sound clear :)
Here is example output that I expect to get:
ORDERID OPERATORID GROUPID DESCRIPTION STATUS CASH NEWOPERATORID
------------------------------------------------------------------------
1 1 1 small_order 1 300 2
2 1 1 another_order 1 0 1
9 5 1 xxxxxxxxxxx 1 50 4
10 (null) 2 xxxxxxxxxxx 1 150 4
11 (null) 3 xxxxxxxxxxx 1 -50 2
12 4 1 xxxxxxxxxxx 1 200 1
How can I (if I can at all) assign Operators to my Orders so that sum or Cash will be closest to average
If I'm understanding this right, could you get the result you want by ordering the Cash column by the biggest, then the smallest, then the next biggest, then the next smallest, etc. Like this:
ROW_NUMBER() over (order by CASE WHEN CashOrder % 2 = 1 then Cash else -Cash END) as splitCash
where you've provided CashOrder lower in the query with
ROW_NUMBER() over (ORDER by CASH) as CashOrder
Then you specify each of your operators depending on this split value, ie (for three operators):
splitCash%3 +1