SQL calculated column with case statement and table join - sql

Okay, I've done some web digging and cannot seem to find how to make this situation work or even if it is possible.
I am using SQL Server 2008 and have a calculated column where i am trying to figure some values regarding foreign currency exchange rates.
Although one single table might be easier in the long run, I would like to use the correct solution which involves two different tables.
table t1 houses a log of transactions. table t2 houses basic information for a given currency. The two tables can be joined using the currencyCode attribute.
The sticky part comes with currency dependent calculations. Presently the calculations only differ for CR1 CR2 CR3 or CR4 but in the future as more currencies are added, the potential exists for the new currencies to differ. So ideally, a new row would be added to t2 and a bit flag (flagCurrencyCalc) set that specifies the alternate calculation be used or not used.
So the present formula looks something like this:
(case when [currencyCode]='CR1' OR [currencyCode]='CR2' OR [currencyCode]='CR3' OR [currencyCode]='CR4' then (formula1) else (formula2) end)
As you can see, i would have to manually go in and alter the formual by adding another OR statement with the new currencyCode.
Would it be possible to do something along the line of:
(case when t2.flagCurrencyCalc=True for a given currency code in a record found in t1 then (formula1) else (formula2) end)
???

You cannot access another table in a computed column directly (see here).
Here are some options.
You can simplify your formula using in:
(case when currencyCode in ('CR1', 'CR2', 'CR3', 'CR4')
then (formula1)
else (formula2)
end)
You can use a view:
create view vw_tablename as
select t.*,
(case when currencyCode in (select currencyCode from ListOfCurrencies where . . . )
then (formula1)
else (formula2)
end)
from t;
You can define a function to check the currency.

Yes it is possible. If the other table you are proposing is say
CurrencyFlag (curr_code varchar(5), flag bit)
curr_code flag
--------- -----
C1 1
C2 0
...
then your case can have a sub query
SELECT t1.col1, t1.col2,
CASE
WHEN (SELECT flag FROM CurrencyFlag t2 WHERE t2.curr_code = t1.currencyCode) = 1
THEN formula1
ELSE formula2
END , t1.col3
FROM
table1 t1

Related

Adding a column derived from a CASE function to a normal table column

I am trying to add the output of a column derived from a CASE statement ([Gift Aid Total] to a standard table column (bd.paymentamount). In this case, the derived column equals a monetary value and I want to add it to another monetary value within a table.
I have tried standard SQL code (see below) to add the two columns together.
select co.contacttype, co.firstname+' '+co.keyname [Name], bd.incometype, bd.paymentamount,
CASE WHEN (gd.GADstatus)='Active' THEN bd.paymentamount*0.25
ELSE '0'
END AS [GAT],
[GAT]+bd.paymentamount,
bd.dateofpayment [Payment Date]
from contact co
left join batchdetail bd on co.serialnumber=bd.serialnumber
left join GIFTAID_CURRENTDECLARATION AS gd ON bd.SERIALNUMBER=gd.SERIALNUMBER
where bd.DATEOFPAYMENT BETWEEN '2016-09-01' AND '2019-08-31'
When I try to add these two columns together I receive an error saying Invalid column name 'GAT'.
I would like the code to add both columns together and show the total in a new third column.
SQL statements cannot refer to an alias defined in the SELECT in the same query's SELECT, FROM, or WHERE clauses (and others depending on the database). For the SELECT the reason is simple: SQL does not guarantee the order of evaluation of expressions.
This used to be solved using CTEs or subqueries. My preferred method is a lateral join. The syntax depends on the database. In SQL Server, it looks like this:
select co.contacttype, (co.firstname + ' ' + co.keyname) as [Name],
bd.incometype, bd.paymentamount, v.gat,
v.gat + bd.paymentamount,
bd.dateofpayment as [Payment Date]
from contact co left join
batchdetail bd
on co.serialnumber = bd.serialnumber left join
GIFTAID_CURRENTDECLARATION gd
on bd.SERIALNUMBER = gd.SERIALNUMBER cross apply
(values (CASE WHEN gd.GADstatus ='Active' THEN bd.paymentamount * 0.25
ELSE 0 END)
) v(gat)
where bd.DATEOFPAYMENT BETWEEN '2016-09-01' AND '2019-08-31'
You cant use an alias in the same level because all the field are process at the same time and at that moment the alias still doesn't exists
This is wrong
SELECT 1 as one, one + one as two
Instead you need to first create the alias and then use in in a subquery
SELECT one + one as two
FROM ( SELECT 1 as one ) as t
SQL DEMO

SQL: Add multiple rows from one table to another when no data for that date in new table

I'm trying to update medical data from one table to another after switching from one system to another. We have two tables, for simplicity I'll make this a simple example. There are many columns in these tables in reality (not just 5).
Table1:
name, date, var1, var2, var3
Table2:
name, date, var1a, var2a, var3a
I want to transfer data from Table 1 to Table 2 for any rows where there isn't previous data for that date, where var1 = var1a, etc (same columns with different names).
I was trying to do something with a loop, but realized that may not be necessary.
I had gotten this far but keep wasn't sure if this was ok:
UPDATE Table2 VALUES (date, var1a, var2a, var3a)
SELECT date, var1, var2, var3 FROM Table1
Is that correct syntax so far? Or do I need to map the variables to translate var1 into var1a, etc?
How do I add a check to make sure I don't overwrite any data already in Table1? I don't want to add data if there is already data for that date/name combination.
Thanks!
You can INSERT into TABLE2 all values from TABLE1 that do not already exist in Table2:
INSERT INTO Table2 (date, var1a, var2a, var3a)
SELECT date, var1, var2, var3
FROM Table1 t1
WHERE NOT EXISTS (SELECT 1 FROM Table2 t2 WHERE t2.date = t1.date)
Already existing values are specified by comparing the date column. You can add any other predicates in the SELECT subquery of the NOT EXISTS expression to suit your needs.
You could use an update with a join. And you dont need to update the date column since that's what you are using to find the matches in the 2 tables.
Either you generate a dynamic query based on the empty/null valued columns, or you could do something like the below, which puts the same value in the column if it exists in table2 or else puts the corresponding value from table1.
The below approach requires less logic and easier to implement but will produce IO equivalent to updating the entire table.
update tbl2
set val1a=isnull(val1a,val1)
, val2a=isnull(val2a,val2)
, val3a=isnull(val3a.val3)
from table1 tbl1
inner join table2 tbl2
on tbl1.name=tbl2.name
and tbl1.date=tbl2.date
Considerations:
The approach requires less logic and easier to implement but will produce IOs equivalent to updating the entire table2. If you have a smallish table i would go with this approach.
If its a big table then you should look into building specific query sets to reduce IO
This code is tested in Access but something very similar should work in SQL Server 2012:
UPDATE Table2 RIGHT JOIN Table1 ON Table2.date = Table1.date
SET Table2.name = Table1.name, Table2.date = Table1.date, Table2.var1 = Table1.var1a, Table2.var2 = Table1.var2a, Table2.var3 = Table1.var3a
WHERE (Table2.date Is Null);
Explanation: this uses a right join so that within the query all the data from Table1 is present and where there is a matched date for Table2 that data is present too. We then ignore all cases where there is any data for Table2 and update the query in all other cases - the update in fact inserts new data into Table2.

Can I use a table column name as a search argument in db2 "like" or "contain" operator/function

I have two tables: one containing a column (Description) that I would like to search using values in a column (keyword) in another table. I would like to perform something like
select table1.description, tabl2.keyword,
case when
table1.description like `'''%'||table2.keyword||'%''' then 1
-- or contains(table1.description, table2.keyword)
else
0
end
as found
from table1, table2
The documentation for Contain function seems to indicate that the search parameter (table2.keyword in my case) has to be a constant (I suspect "like" also has similar constraints).
The error messages I get seem to indicate this constraint.
Is there a solution that I can use?
You can do it with like. Your query should work:
select table1.description, tabl2.keyword,
(case when table1.description like '''%'||table2.keyword||'%'''
then 1 else 0
end) as found
from table1 cross join table2;
I prefer explicit joins, cross join instead of ,.

How to assign a value to a casted column in Oracle

I am wondering whether is possible to assign a value to a casted column in SQL depending on real table values.
For Example:
select *, cast(null as number) as value from table1
where if(table1.id > 10 then value = 1) else value = 0
NOTE: I understand the above example is not completely Oracle, but, it is just a demonstration on what I want to accomplish in Oracle. Also, the above example can be done multiple ways due to its simplicity. My goal here is to verify if it is possible to accomplish the example using casted columns (columns not part of table1) and some sort of if/else.
Thanks,
Y_Y
select table1.*, (case when table1.id > 10 then 1 else 0 end) as value
from table1

MS SQL update table with multiple conditions

Been reading this site for answers for quite a while and now asking my first question!
I'm using SQL Server
I have two tables, ABC and ABC_Temp.
The contents are inserted into the ABC_Temp first before making its way to ABC.
Table ABC and ABC_Temp have the same columns, except that ABC_Temp has an extra column called LastUpdatedDate, which contains the date of the last update. Because ABC_Temp can have more than 1 of the same record, it has a composite key of the item number and the last updated date.
The columns are: ItemNo | Price | Qty and ABC_Temp has an extra column: LastUpdatedDate
I want to create a statement that follows the following conditions:
Check if each of the attributes of ABC differ from the value of ABC_Temp for records with the same key, if so then do the update (Even if only one attribute is different, all other attributes can be updated as well)
Only update those that need changes, if the record is the same, then it would not update.
Since an item can have more than one record in ABC_Temp I only want the latest updated one to be updated to ABC
I am currently using 2005 (I think, not at work at the moment).
This will be in a stored procedure and is called inside the VBscript scheduled task. So I believe it is a once time thing. Also I'm not trying to sync the two tables, as the contents of ABC_Temp would only contain new records bulk inserted from a text file through BCP. For the sake of context, this will be used with in conjunction with an insert stored proc that checks if records exist.
UPDATE
ABC
SET
price = T1.price,
qty = T1.qty
FROM
ABC
INNER JOIN ABC_Temp T1 ON
T1.item_no = ABC.item_no
LEFT OUTER JOIN ABC_Temp T2 ON
T2.item_no = T1.item_no AND
T2.last_updated_date > T1.last_updated_date
WHERE
T2.item_no IS NULL AND
(
T1.price <> ABC.price OR
T1.qty <> ABC.qty
)
If NULL values are possible in the price or qty columns then you will need to account for that. In this case I would probably change the inequality statements to look like this:
COALESCE(T1.price, -1) <> COALESCE(ABC.price, -1)
This assumes that -1 is not a valid value in the data, so you don't have to worry about it actually appearing there.
Also, is ABC_Temp really a temporary table that's just loaded long enough to get the values into ABC? If not then you are storing duplicate data in multiple places, which is a bad idea. The first problem is that now you need these kinds of update scenarios. There are other issues that you might run into, such as inconsistencies in the data, etc.
You could use cross apply to seek the last row in ABC_Temp with the same key. Use a where clause to filter out rows with no differences:
update abc
set col1 = latest.col1
, col2 = latest.col2
, col3 = latest.col3
from ABC abc
cross apply
(
select top 1 *
from ABC_Temp tmp
where abc.key = tmp.key
order by
tmp.LastUpdatedDate desc
) latest
where abc.col1 <> latest.col1
or (abc.col2 <> latest.col2
or (abc.col1 is null and latest.col2 is not null)
or (abc.col1 is not null and latest.col2 is null))
or abc.col3 <> latest.col3
In the example, only col2 is nullable. Since null <> 1 is not true, you have to check differences involving null using the is null syntax.