Getting an 'Update Case When' statement to do an imperfect match? - postgresql

Apologies if the title is confusing, unsure of the technical term. I'm working on getting a specific text, when inside a cell, to trigger a less sensitive match for updating the row. So when there's a "CH-%" inside of the cell, instead of matching to that particular "CH-N" or "CH-S" it'll look for any "CH-%" and then get back that value if the other columns match as well.
Here's my code:
update t_vessel_list_ballast
SET voyage_time = cast(b.travel_time as INTEGER),
time_source = 'region'
FROM t1_region b
WHERE CASE
WHEN cast(b.travel_time as integer) > 100 THEN voyage_time = null
WHEN depart_region ilike 'CH-%' then
('CH-%' ilike b.region
AND t_vessel_list_ballast.dest_region = b.region_arrive
AND t_vessel_list_ballast.voyage_time is null)
OR (t_vessel_list_ballast.dest_region = b.region
AND 'CH-%' ilike b.region_arrive
AND t_vessel_list_ballast.voyage_time is null)
ELSE
(t_vessel_list_ballast.depart_region = b.region
AND t_vessel_list_ballast.dest_region = b.region_arrive
AND t_vessel_list_ballast.voyage_time is null)
OR (t_vessel_list_ballast.dest_region = b.region
AND t_vessel_list_ballast.depart_region = b.region_arrive
AND t_vessel_list_ballast.voyage_time is null) END;
--end
This code will run, without any errors, but not do the intended result. Any suggestions on how to get it to do what i'm asking?

I believe you have a backwards condition..
change
'CH-%' ilike b.region
to
b.region ilike 'CH-%'

Related

Is there a way to make this case statement work?

I am getting the following error with the code below:
ERROR: argument of AND must be type boolean, not type character varying
LINE 9: and case
^
SQL state: 42804
Character: 300
This code does a few things, but what I am having trouble with is the case statment. I want this piece to look for instances where the first 11 characters of two strings match. If that is not true for a given record, then look at the first 10 characters, then 9, then 8. After that, null is an acceptable result.
select cm.course_id, cm.course_name, cmp.course_id as parentcourse,
(select cmm.course_id
from course_main cmm
where cmm.course_id ilike '%Master%'
and cmm.course_id not ilike '%Ground%'
and cmm.course_id not ilike '%Surprise%'
and cmm.course_id not ilike '%emba%'
and cmm.row_status != 2
and case
when left(cm.course_id,11) = left(cmm.course_id,11)
then cmm.course_id
else
case
when left(cm.course_id,10) = left(cmm.course_id, 10)
then cmm.course_id
else
case
when left(cm.course_id,9) = left(cmm.course_id, 9)
then cmm.course_id
else
case
when left(cm.course_id,8) = left(cmm.course_id,8)
then cmm.course_id
end
end
end
end) as mastercourse
from course_main as cm
left join course_course cc
on cc.crsmain_pk1 = cm.pk1
left join course_main cmp
on cmp.pk1 = cc.crsmain_parent_pk1
where cm.course_id ilike '%-ES-2020-%'
and cm.row_status != 2
and cmp.course_id is null
order by cm.course_id;
Thank you for the assistance, Z4. I took a shot at applying your suggestions and was able to get past the error. The problem with these strings is that I am trying to match something like:
'NRSG-46009-ES-2020-OA' to 'NRSG-46009-Master-Online-Content' in an environment that also contains these:
'NRSG-46006-Master-Online-Content'
'NRSG-46003-Master-Online-Content'
'NRSG-4600-Master-Online-Content'
If I look at the first 8 characters before looking at the first 11, I will get mismatches. So, I am looking at 11 first. If there is nothing that matches, look at the first ten, and so on. Strings of 8 are the floor in our ID schema with examples like this:
'IT-7003-ES-2019-AE' that needs to be matched with 'IT-7003-Master-Online-Content'
Anyway, I took your advice and ran the following:
select distinct cm.course_id, cm.course_name, cmp.course_id as parentcourse,
case
when left(cm.course_id,11) = left(cmm.course_id,11)
then cmm.course_id
else
case
when left(cm.course_id,10) = left(cmm.course_id, 10)
then cmm.course_id
else
case
when left(cm.course_id,9) = left(cmm.course_id, 9)
then cmm.course_id
else
case
when left(cm.course_id,8) = left(cmm.course_id,8)
then cmm.course_id
end
end
end
end as mastercourse
from course_main cm
left join course_course cc
on cc.crsmain_pk1 = cm.pk1
left join course_main cmp
on cmp.pk1 = cc.crsmain_parent_pk1
left join course_main cmm
on cm.pk1 = cm.pk1
where cm.course_id ilike '%-ES-2020-%'
and cm.row_status != 2
and cmp.course_id is null
and cmm.course_id ilike '%Master%'
and cmm.course_id not ilike '%Ground%'
and cmm.course_id not ilike '%Surprise%'
and cmm.course_id not ilike '%emba%'
and cmm.row_status != 2
order by cm.course_id;
This seems to be working, but I am getting duplicate results:
results of 'successful' query
Any ideas on how I can exclude the duplicates?
First, there is really no need to use such a complex case statement. If the first 8 characters do not match, then certainly the first 9, or 10 or 11 are not going to match either, right? So just get rid of the whole thing and check only the first 8:
CASE
WHEN LEFT(cm.course_id, 8) = LEFT(cmm.course_id, 8)
THEN cmm.course_id
END
Next, the CASE statement needs to come out of the WHERE clause and into the SELECT list (this is what is causing the boolean type error):
select
[...]
(
select
CASE
WHEN LEFT(cm.course_id, 8) = LEFT(cmm.course_id, 8)
THEN cmm.course_id
END
from
course_main cmm
where
cmm.course_id ilike '%Master%'
and cmm.course_id not ilike '%Ground%'
and cmm.course_id not ilike '%Surprise%'
and cmm.course_id not ilike '%emba%'
and cmm.row_status != 2
) as mastercourse
FROM
[...]
I don't know anything about the data that you're trying to capture here, but I would also strongly suggest taking some time to rethink the use of a correlated subquery. Even if this is correctly describing the data set you want, the subquery is really inefficient. I would guess that it can probably be replaced entirely by a LEFT JOIN in the main query body (likely a self join from course_main to itself).

Setting a variable in T-SQL from a Query

So I have this query:
select ens_use_new_models_bit from cfo_transaction
inner join dbo.cfo_trans_entity_rel on te_tr_transaction_id=tr_transaction_id
inner join cfo_tran_quote on tq_tr_transaction_id = tr_transaction_id
left outer join cfo_engine_sponsor on ens_rs_sponsor_id = te_co_re_entity_id
where te_rv_rel_type_id=713 and tq_tran_quote_id = 3
It returns a bit value, which can also be NULL. I hardcoded 3 for testing but in reality another proc passes this value in, but that's not important here.
Now, in a stored proc, I need to set a variable that's declared in the proc:
SET #vRtn = NULL
as the string - either 'VBEngines' or 'WFModels', or keep it NULL if the bit from above returns NULL.
'VBEngines' if the bit is off, 'WFModels' if the bit is on.
Then after that, I need to perform a T-SQL condition on the value, to see if it's NULL or not. How would I do this? I'm so bad with SQL.
Thanks.
Assuming that you don't need the bit value itself stored in a variable as that was just a means to an end then this should do it.
select #vRtn = case ens_use_new_models_bit
when 0 then 'VBEngines'
when 1 then 'WFModels'
end /*Implicit Else NULL case*/
from cfo_transaction ...
In the case 0 rows are returned no assignment is made so #vRtn keeps its initial value,
declare #newmodelsbit bit, #vRtn varchar(10)
select #newmodelsbit = ens_use_new_models_bit from cfo_transaction
inner join dbo.cfo_trans_entity_rel on te_tr_transaction_id=tr_transaction_id
inner join cfo_tran_quote on tq_tr_transaction_id = tr_transaction_id
left outer join cfo_engine_sponsor on ens_rs_sponsor_id = te_co_re_entity_id
where te_rv_rel_type_id=713 and tq_tran_quote_id = 3
if #newmodelsbit is null
begin
set #vRtn = null
end
else
begin
if #newmodelsbit = 1 --bit is on
begin
set #vRtn = 'WFModels'
end
else -- bit is off
begin
set #vRtn = ' VBEngines'
end
end

How do I use T-SQL's Case/When?

I have a huge query which uses case/when often. Now I have this SQL here, which does not work.
(select case when xyz.something = 1
then
'SOMETEXT'
else
(select case when xyz.somethingelse = 1)
then
'SOMEOTHERTEXT'
end)
(select case when xyz.somethingelseagain = 2)
then
'SOMEOTHERTEXTGOESHERE'
end)
end) [ColumnName],
Whats causing trouble is xyz.somethingelseagain = 2, it says it could not bind that expression. xyz is some alias for a table which is joined further down in the query. Whats wrong here? Removing one of the 2 case/whens corrects that, but I need both of them, probably even more cases.
SELECT
CASE
WHEN xyz.something = 1 THEN 'SOMETEXT'
WHEN xyz.somethingelse = 1 THEN 'SOMEOTHERTEXT'
WHEN xyz.somethingelseagain = 2 THEN 'SOMEOTHERTEXTGOESHERE'
ELSE 'SOMETHING UNKNOWN'
END AS ColumnName;
As soon as a WHEN statement is true the break is implicit.
You will have to concider which WHEN Expression is the most likely to happen. If you put that WHEN at the end of a long list of WHEN statements, your sql is likely to be slower. So put it up front as the first.
More information here: break in case statement in T-SQL
declare #n int = 7,
#m int = 3;
select
case
when #n = 1 then
'SOMETEXT'
else
case
when #m = 1 then
'SOMEOTHERTEXT'
when #m = 2 then
'SOMEOTHERTEXTGOESHERE'
end
end as col1
-- n=1 => returns SOMETEXT regardless of #m
-- n=2 and m=1 => returns SOMEOTHERTEXT
-- n=2 and m=2 => returns SOMEOTHERTEXTGOESHERE
-- n=2 and m>2 => returns null (no else defined for inner case)
If logical test is against a single column then you could use something like
USE AdventureWorks2012;
GO
SELECT ProductNumber, Category =
CASE ProductLine
WHEN 'R' THEN 'Road'
WHEN 'M' THEN 'Mountain'
WHEN 'T' THEN 'Touring'
WHEN 'S' THEN 'Other sale items'
ELSE 'Not for sale'
END,
Name
FROM Production.Product
ORDER BY ProductNumber;
GO
More information - https://learn.microsoft.com/en-us/sql/t-sql/language-elements/case-transact-sql?view=sql-server-2017

field appears null or empty sting but I can not select it

select *
from ss.mailer_data
where
id = 249122
and address_3 like'%%'
will not hit on address_3. I've tried changing the last line to
and address_3 is null
and address_3 = ''
and address_3 = ' '
I tried using char_length, ascii functions and they return nothing for that filed value. Anyone have any ideas?
could also be that id = 249122 is not true for that combination. What happens when you do
select *
from ss.mailer_data
where address_3 is null
or
select *
from ss.mailer_data
where address_3 = ''
NULL is a special case - It's undefined but if that were your problem, Is Null should've found it
Can I ask you what data IS actually stored in Address3?
If you just want all records where address3 is empty/null,
select *
from ss.mailer_data
where
RTRIM(COALESCE(address_3, '')) =''
NB: LIKE is computationally expensive so use it carefully. Also, like '%%' is identical to Like '%'
Are you sure you had the right Id?

SELECT..CASE - Refactor T-SQL

Can I refactor the below SQL CASE statements into single for each case ?
SELECT
CASE RDV.DOMAIN_CODE WHEN 'L' THEN CN.FAMILY_NAME ELSE NULL END AS [LEGAL_FAMILY_NAME],
CASE RDV.DOMAIN_CODE WHEN 'L' THEN CN.GIVEN_NAME ELSE NULL END AS [LEGAL_GIVEN_NAME],
CASE RDV.DOMAIN_CODE WHEN 'L' THEN CN.MIDDLE_NAMES ELSE NULL END AS [LEGAL_MIDDLE_NAMES],
CASE RDV.DOMAIN_CODE WHEN 'L' THEN CN.NAME_TITLE ELSE NULL END AS [LEGAL_NAME_TITLE],
CASE RDV.DOMAIN_CODE WHEN 'P' THEN CN.FAMILY_NAME ELSE NULL END AS [PREFERRED_FAMILY_NAME],
CASE RDV.DOMAIN_CODE WHEN 'P' THEN CN.GIVEN_NAME ELSE NULL END AS [PREFERRED_GIVEN_NAME],
CASE RDV.DOMAIN_CODE WHEN 'P' THEN CN.MIDDLE_NAMES ELSE NULL END AS [PREFERRED_MIDDLE_NAMES],
CASE RDV.DOMAIN_CODE WHEN 'P' THEN CN.NAME_TITLE ELSE NULL END AS [PREFERRED_NAME_TITLE]
FROM dbo.CLIENT_NAME CN
JOIN dbo.REFERENCE_DOMAIN_VALUE RDV
ON CN.NAME_TYPE_CODE = RDV.DOMAIN_CODE AND RDV.REFERENCE_DOMAIN_ID = '7966'
No, you will require 8 separate statements as case and other such variants can only be used in a select to modify the results of a single column, not a series of columns.
If RDV.DOMAIN_COD can only by 'P' or 'L' use NULLIf. It's cleaner.
NULLIF ( expression , expression )
NULLIF is equivalent to a searched CASE expression in which the two expressions are equal and the resulting expression is NULL.
SELECT
NullIf('P', RDV.DOMAIN_CODE) AS [LEGAL_FAMILY_NAME],
...
NullIf('L', RDV.DOMAIN_CODE) AS [PREFERRED_FAMILY_NAME],
...
Since a CASE expression returns a single value, you cannot take eight CASE expressions returning 8 values and make a single CASE expression that returns all eight.
A less efficient alternative with no cases:
SELECT LEGAL_FAMILY_NAME, LEGAL_GIVEN_NAME, LEGAL_MIDDLE_NAMES, LEGAL_NAME_TITLE,
PREFERRED_FAMILY_NAME, PREFERRED_GIVEN_NAME, PREFERRED_MIDDLE_NAMES, PREFERRED_NAME_TITLE
FROM dbo.REFERENCE_DOMAIN_VALUE RDV
LEFT OUTER JOIN
( SELECT
NAME_TYPE_CODE,
FAMILY_NAME AS [LEGAL_FAMILY_NAME],
GIVEN_NAME AS [LEGAL_GIVEN_NAME],
MIDDLE_NAMES AS [LEGAL_MIDDLE_NAMES],
NAME_TITLE AS [LEGAL_NAME_TITLE]
FROM dbo.CLIENT_NAME
WHERE NAME_TYPE_CODE = 'L') LN ON RDV.DOMAIN_CODE = LN.NAME_TYPE_CODE
LEFT OUTER JOIN
( SELECT
NAME_TYPE_CODE,
FAMILY_NAME AS [PREFERRED_FAMILY_NAME],
GIVEN_NAME AS [PREFERRED_GIVEN_NAME],
MIDDLE_NAMES AS [PREFERRED_MIDDLE_NAMES],
NAME_TITLE AS [PREFERRED_NAME_TITLE]
FROM dbo.CLIENT_NAME
WHERE NAME_TYPE_CODE = 'P') PN ON RDV.DOMAIN_CODE = PN.NAME_TYPE_CODE
WHERE RDV.REFERENCE_DOMAIN_ID = '7966'
You could also use a temp table or table variable with all 8 columns and then do two inserts. You could also use a UNION ALL. My guess is that the 8 case statements are the most efficient way. This is especially true if you have some key where you will want some type of ClientID, Legal Names, Preferred Names so you will wrap a MAX around the cases or something and group by a ClientID......
You could generate the SQL script using syscols/INFORMATION_SCHEMA.columns with two case whens for each column 'CASE WHEN DOMAIN_CODE = ''P'' THEN ' + COLUMN_NAME + ' ELSE NULL END AS PREFERRED_' + COLUMN_NAME and then another for L. You could make a LOOP so that the same code executes once for P and once for L and get it down to one loop. Then you could EXEC the string directly, or PRINT it and put it into your SQL script. Anyway for just 8 columns I would cut/paste the case statements...
But anyway in general T-SQL is limited on being able to do for eaches over columns. Either you generate the script using a code generator (done in t-sql or another programming language) or you rethink your problem in another way. But many times you get better performance from duplicate cut/paste code. And many times it isn't worth the hassle of writing an external code generator (ie just for 8 case statements).