Populate a column via multi-dimensional lookup in SQL? - sql

I have the following two tables:
TABLE_A:
Date
USD
EUR
2020-01-31
1.11
0.89
2020-02-28
1.15
0.93
TABLE_B:
Date
Currency
Amount
2020-01-31
USD
NULL
2020-02-29
EUR
NULL
I want to populate the Amount field in TABLE_B with the corresponding amount in TABLE_A dependent on the matching date and currency. Is there a way to perform this multi-dimensional lookup in SQL?

The only join I can see that is common to both tables is the date value, you can join on that and then use a CASE expression to choose the appropriate value from tableA based on the value in tableB
SELECT CASE WHEN b.Currency = 'USD' THEN a.USD ELSE a.EUR END as [Amount]
FROM tableA AS a
INNER JOIN tableB AS b
ON b.Date = a.Date

Related

T-SQL count number of different values across multiple columns

I have an SQL database which contains a table with four different currency columns, I need to determine how many different currencies each record has across these four columns, for example..
Record ID
Curr1
Curr2
Curr3
Curr4
CurrencyCount
1
GBP
USD
GBP
GBP
2
2
GBP
EUR
GBP
USD
3
3
GBP
GBP
GBP
GBP
1
4
GBP
GBP
GBP
EUR
2
How can I determine the "CurrencyCount" / count of currencies against each record? I can't think how to approach this
Thank you for any help
You can use a Table Value Constructor referencing columns from the outer row and then COUNT (DISTINCT - no need for any expanding out and collapsing with GROUP BY
SELECT *,
CurrrencyCount = (SELECT COUNT (DISTINCT Currency) FROM (VALUES (CURR1), (CURR2), (CURR3), (CURR4)) AS x (Currency))
FROM YourTable
This would do the work. But not sure how will be the performance if there is a large amount of data.
WITH CTE as (
SELECT '1' as RecID,'GBP' as CURR1,'USD' as CURR2,'GBP' as CURR3, 'GBP' as CURR4
UNION ALL
SELECT '2' ,'GBP' ,'EUR' ,'GBP','USD'
UNION ALL
SELECT '3' ,'GBP' ,'GBP' ,'GBP','GBP'
UNION ALL
SELECT '4' ,'GBP' ,'GBP' ,'GBP','EUR'
)
SELECT
RecID
,CURR1,CURR2,CURR3,CURR4
,COUNT(DISTINCT Currency) as CurrrencyCount
FROM CTE
CROSS APPLY (VALUES (CURR1), (CURR2), (CURR3), (CURR4)) as x (Currency)
GROUP BY RecID,CURR1,CURR2,CURR3,CURR4
Only run this its also work
SELECT
RecordID
,Curr1,Curr2,Curr3,Curr4
,COUNT(DISTINCT Currency) as Currency
FROM [TABLE_NAME]
CROSS APPLY (VALUES (Curr1), (Curr2), (Curr3),(Curr4)) as x (Currency)
GROUP BY RecordID,Curr1,Curr2,Curr3,Curr4

Netezza - update one table with max data from another table

I have a table in netezza that I need to update. The columns I am working with are
TABLE A
ID_NO
ENTRY_DATE
PRICE
TABLE B
ID_NO
START_DATE
END_DATE
PRICE
So an example of the data would look like this:
TABLE A
ID_NO
ENTRY_DATE
PRICE
123
2020-05-01
123
2020-08-15
TABLE B
ID_NO
START_DATE
END_DATE
PRICE
123
2019-01-01
2019-11-01
$7.64
123
2020-04-30
2020-05-02
$6.19
123
2020-04-15
2020-08-30
$2.19
I need to update the PRICE in TABLE A to be the max PRICE from TABLE B where a.ENTRY_DATE is between b.START_DATE and b.END_DATE. So the final table should look like this:
TABLE A
ID_NO
ENTRY_DATE
PRICE
123
2020-05-01
$6.19
123
2020-08-15
$2.19
This is what I have so far, but it just ends up taking the max price that fits either row rather than doing the calculation for each row:
update TABLE_A
set PRICE=(select max(b.PRICE)
from TABLE_B b
inner join TABLE_A a on a.ID_NO=b.ID_NO
where a.ENTRY_DATE between b.START_DATE and b.END_DATE)
I don't have access to Netezza, but a usual format would be to use a correlated sub-query.
That is, instead of including TABLE_A again in the query, you refer to the outer reference to TABLE_A...
update
TABLE_A
set
PRICE = (
select max(b.PRICE)
from TABLE_B b
where TABLE_A.ID_NO = b.ID_NO
and TABLE_A.ENTRY_DATE between b.START_DATE and b.END_DATE
)
In this way, the correlated-sub-query is essentially invoked once for each row in TABLE_A and that invocation uses the current row from TABLE_A as its parameters.
An alternative could be...
update
TABLE_A
set
PRICE = revised.PRICE
from
(
select a.ID_NO, a.ENTRY_DATE, max(b.PRICE) AS PRICE
from TABLE_B b
inner join TABLE_A a on a.ID_NO=b.ID_NO
where a.ENTRY_DATE between b.START_DATE and b.END_DATE
group by a.ID_NO, a.ENTRY_DATE
)
AS revised
where
TABLE_A.ID_NO = revised.ID_NO
and TABLE_A.ENTRY_DATE = revised.ENTRY_DATE

Remove Transaction using ID and Date range in Redshift SQL

I want to create a query in redshift that will remove transaction items (Table A) base on its validity date (Table B)
Lets call it table A and Table B
Table A contains all transaction of Items
Table A
Item_Code Date
1. I0001 2019-12-01
2. I0002 2019-12-02
3. I0001 2020-01-01
4. I0003 2020-01-01
then Table 2 contains Item validity date
Table B
Item_Code Valid_From Valid_To
1. I0001 2019-01-01 2019-12-31
2. I0002 2019-01-01 2019-12-31
3. I0003 2019-01-01 2019-12-31
and my expected output will be
Item_Code Date
1. I0001 2019-12-01
2. I0002 2019-12-02
THis will give your expected results
Select * from TableA a where not exists(select 1 from TableB b on a.Date between b.Valid_From
and Valid_To)
And once you sure thats what you want, you delete it with
delete from TableA a where not exists(select 1 from TableB b on a.Date between b.Valid_From
and Valid_To)
I would join both tables and insert the validity as a join condition.
This will delete all invalid items or you can reverse the validity condition to detect which items are invalid
SELECT A.ITEM_CODE,A.DATE
FROM Table a
INNER JOIN TABLE B
ON A.ITEM_CODE=B.ITEM_CODE AND A.DATE=> B.Valid_from AND A.date <= B.Valid_to

Link on two tables if not all values between fields match in PostgreSQL?

I have two tables
exchange_rates:
curr1 curr2 rate
USD GBP 0.81
EUR GBP 0.98
transactions
TIMESTAMP user curr amt
2017-01-01 u1 EUR 89
2017-01-01 u2 GBP 3
2017-01-03 u2 USD 10
I want to link exchange_rates and transactions and multiply amt by the corresponding exchange rate in GBP in exchange_rates. e.g. in line 3 of transactions we would multiply 10 by 0.81 to get 8.1. BUT if the amount is in GBP in the transactions table I want to leave that unchanged.
I have tried to use CASE and link the two table like this
select
trans.TIMESTAMP, trans.user
case
when trans.currency != "GBP" then trans.amt*er.rate
else trans.amt
end as "Converted Amount"
from exchange_rates er, transactions trans
where trans.curr = er.curr1
But this doesn't work when curr in transactions is GBP (line 2) since there is not curr1=GBP in exchange_rates... can anyone advise what the logic would be here to solve this?
DESIRED RESULT:
TIMESTAMP user converted amt
2017-01-01 u1 87.22
2017-01-01 u2 3
2017-01-03 u2 8.1
Simply add a record in exchange_rates that defines a 1 to 1 exchange rate for GBP:
curr1 curr2 rate
GBP GBP 1
Aside from that you can also use a left outer join to include all the matching records in the source table, regardless if the joined table matched: http://www.postgresqltutorial.com/postgresql-left-join/
select
trans.TIMESTAMP, trans.user,
trans.amt * coalesce(er.rate, 1) as "Converted Amount"
from transactions trans
left join exchange_rates er on er.curr1 = trans.curr
I think you want something like this:
select t.TIMESTAMP, t.user,
t.amt * coalesce(er.rate, 1) as converted_amount
from transactions t left join
exchange_rates er
on t.curr = er.curr1 and er.curr2 = 'GBP';
Why does this look a bit different from your query?
First, it uses a left join. Second, it compares both currencies. It also simplifies the logic for the lookup.

sql Query to find different record in a column

I have table with 5 OR 6 COLUMNS and I need to use the below 2 columns to get the result
col1 col2
Acc1 USD
ACC1 GBP
ACC1 EUR
ACC2 USD
Result:
I need to find out if a acc has more than 2 currency, but the base currency is USD. I need to find out those records which has USD plus other currency if I have only USD accounts then it should not come in my result.
With the information provided this could be an answer:
SELECT col1
FROM tab
GROUP BY col1
HAVING count(*) > 1
Not the best solution but should do the job.
with cte as
(
SELECT t1.[col1],t1.[col2],(select count(t2.col1) from accounts t2 where t2.col1=t1.col1) as AllCurrency
from accounts t1
)
SELECT distinct cte.col1 from cte where cte.AllCurrency>1