REPLACE not doing what I need in SQL - sql

I've got a query pulling data from a table. In one particular field, there are several cases where it is a zero, but I need the four digit location number. Here is where I'm running into a problem. I've got
SELECT REPLACE(locationNbr, '0', '1035') AS LOCATION...
Two issues -
Whoever put the table together made all fields VARCHAR, hence the single quotes.
In the cases where there already is the number 1035, I get 1103535 as the location number because it's replacing the zero in the middle of 1035.
How do I select the locationNbr field and leave it alone if it's anything other than zero (as a VARCHAR), but if it is zero, change it to 1035? Is there a way to somehow use TO_NUMBER within the REPLACE?

SELECT CASE WHEN locationNbr='0' THEN '1035' ELSE locationNbr END AS LOCATION...
REPLACE( string, string_to_replace , replacement_string )
REPLACE looks for a string_to_replace inside a string and replaces it with a replacent_string. That is why you get the undesired behaviour - you are using the wrong function.
CASE WHEN condition THEN result1 ELSE result2 END
CASE checks a condition and if it is true it returns result1 and if it is not it will return result2. This is a simple example, you can write a case statement with more than one condition check.

Don't use replace(). Use case:
(case when locationNbr = '0' then '1035' else locationNbr end)

You can make use of length in Oracle:
select case when length(loacation) = 1 then REPLACE(loacation, '0', '1035') else loacation end as location
from location_test;

Related

PostgreSQL - Assign integer value to string in case statement

I need to select one and only 1 row of data based on an ID in the data I have. I thought I had solved this (For details, see my original question and my solution, here: PostgreSQL - Select only 1 row for each ID)
However, I now still get multiple values in some cases. If there is only "N/A" and 1 other value, then no problem.. but if I have multiple values like: "N/A", "value1" and "value2" for example, then my case statement is not sufficient and I get both "value1" and "value2" returned to me. This is the case statement in question:
CASE
WHEN "PQ"."Value" = 'N/A' THEN 1
ELSE 0
END
I need to give a unique integer value to each string value and then the problem will be solved. The question is: how do I do this? My first thought is to somehow convert the character values to ASCII and sum them up.. but I am not sure how to do that and also worried about performance. Is there a way to very simply assign a value to each string so that I can choose 1 value only? I don't care which one actually... just that it's only 1.
EDIT
I am now trying to create a function to add up the ASCII values of each character so I can essentially change my case statement to something like this:
CASE
WHEN "PQ"."Value" = 'N/A' THEN 9999999
ELSE SumASCII("PQ"."Value")
END
Having a small problem with it though.. I have added it as a separate question, here: PostgreSQL - ERROR: query has no destination for result data
EDIT 2
Thanks to #Bohemian, I now have a working solution, which is as follows:
CASE
WHEN "PQ"."Value" = 'N/A' THEN -1
ELSE ('x'||LPAD(MD5("PQ"."Value"),16,'0'))::bit(64)::bigint
END DESC
This will produce a "unique" number for each value:
('x'||substr(md5("PQ"."Value"),1,8))::bit(64)::bigint
Strictly speaking, there is a chance of a collision, but it's very remote.
If the result is "too big", you could try modulus:
<above-calculation> % 10000
Although collisions would then be a 0.01% chance, you should try this formula against all known values to ensure there are no collisions.
If you don't care which value gets picked, change RANK() to ROW_NUMBER(). If you do care, do it anyway, but also add another term after the CASE statement in ORDER BY, separated by a comma, with the logic you want - for example if you want the first value alphabetically, do this:
...
ORDER BY CASE...END, "PQ"."Value")
...

Assign a case value to a column rather than an alias

This should be a simple one, but I have not found any solution:
The normal way is using an alias like this:
CASE WHEN ac_code='T' THEN 'time' ELSE 'purchase' END as alias
When using alias in conjunction with UNION ALL this causes problem because the alias is not treated the same way as the other columns.
Using an alias to assign the value is not working. It is still treated as alias, though it has the column name.
CASE WHEN ac_code='T' THEN 'time' ELSE 'purchase' END as ac_subject
I want to assign a value to a column based on a condition.
CASE WHEN ac_code='T' THEN ac_subject ='time' ELSE ac_subject='purchase' END
Now I get the error message
UNION types character varying and boolean cannot be matched
How can I assign a value to a column in a case statement without using an alias in the column (shared by other columns in UNION)?
Here is the whole (simplified) query:
SELECT hr_id,
CASE WHEN hr_subject='' THEN code_name ELSE hr_subject END
FROM hr
LEFT JOIN code ON code_id=hr_code
WHERE hr_job='123'
UNION ALL
SELECT po_id,
CASE WHEN po_subject='' THEN code_name ELSE po_subject END
FROM po
LEFT JOIN code ON code_id=po_code
WHERE po_job='123'
UNION ALL
SELECT ac_id,
CASE WHEN ac_code='T' THEN ac_subject='time' ELSE ac_subject='purchase' END
FROM ac
WHERE ac_job='123'
There is no alias in your presented query. You are confusing terms. This would be a column alias:
CASE WHEN hr_subject='' THEN code_name ELSE hr_subject END AS ac_subject
In a UNION query, the number of columns, column names and data types in the returned set are determined by the first row. All appended rows have to match the row type. Column names in appended rows (including aliases) are just noise and ignored. Maybe useful for documentation, nothing else.
The = operator does not assign anything in a SELECT query. It's the equality operator that returns a boolean value. TRUE if both operands are equal, etc. This returns a boolean value: ac_subject='time' Hence your error message:
UNION types character varying and boolean cannot be matched
The only way to "assign" a value to a particular output column in this query is to include it at the right position in the SELECT list.
The information in the question is incomplete, but I suspect you are also confusing the empty string ('') with the NULL value. A distinction that you need to understand before doing anything else with relational databases. Maybe start here. In this case you would rather use COALESCE to provide a default for NULL values:
SELECT hr_id, COALESCE(hr_subject, code_name) AS ac_subject
FROM hr
LEFT JOIN code ON code_id=hr_code
WHERE hr_job = '123'
UNION ALL
SELECT po_id, COALESCE(po_subject, code_name)
FROM po
LEFT JOIN code ON code_id=po_code
WHERE po_job = '123'
UNION ALL
SELECT ac_id, CASE WHEN ac_code = 'T' THEN 'time'::varchar ELSE 'purchase' END
FROM ac
WHERE ac_job = '123'
Just an educated guess, assuming type varchar. You should have added table qualification to column names to clarify their origin. Or table definitions to clarify everything.
The CASE expression is supposed to return a value, e.g. 'time'.
Your value is another expression subject ='time' which is a boolean (true or false).
Is this on purpose? Does the other query you glue with UNION have a boolean in that place, too? Probably not, and this is what the DBMS complains about.
I found the problem.
CASE WHEN hr_subject=’’ THEN code_name ELSE hr_subject END
The columns code_name and hr_subject was different length. This caused the unpredictable result. I think that aliases can work now.
Thank you for your support.

Trying to combine three individual SELECT statements into one main SELECT statement

Here's the problem. There are 3 fields in my table which may contain data with an extra quotation appended at the end. So, I'm trying to run a select statement that will remove this extra character from these fields, IF that extra character exists. I can write 3 individual queries just fine, and they work, but I'm trying to combine them all into one query. Here's what I have so far, and I know it's probably incorrect the way I have it:
Here's the result set that comes back. Notice that all three columns are NULL. They shouldn't be:
Here's an individual query that works for one field at a time:
Can you tell me what I'm doing wrong?
You can remove the WHEREs, as well as the non-correlated subqueries in the select list, and probably simplify it to this...
SELECT
AID
, EID
, STOREID
, [Language]
, 'BrandLabel' = CASE WHEN BrandLabel LIKE '%"'
THEN LEFT(BrandLabel, LEN(BrandLabel) -1)
ELSE BrandLabel
END
, 'Terms' = CASE WHEN Terms LIKE '%"'
THEN LEFT(Terms, LEN(Terms) -1)
ELSE Terms
END
, 'TrackOrderLbl' = CASE WHEN TrackOrderLbl LIKE '%"'
THEN LEFT(TrackOrderLbl, LEN(TrackOrderLbl) -1)
ELSE TrackOrderLbl
END
FROM parallel_Purchase_Email_Content_OMS WITH (NOLOCK)

DB2 SQL - How can I display nothing instead of a hyphen when the result of my case statement is NULL?

All,
I'm writing a query that includes a CASE statement which compares two datetime fields. If Date B is > Date A, then I'd like the query to display Date B. However, if Date B is not > Date A, then the user who will be getting the report created by the query wants the column to be blank (in other words, not contain the word 'NULL', not contain a hyphen, not contain a low values date). I've been researching this today but have not come up with a viable solution so thought I'd ask here. This is what I have currently:
CASE
WHEN B.DTE_LNP_LAST > A.DTE_PROC_ACT
THEN B.DTE_LNP_LAST
ELSE ?
END AS "DATE OF DISCONNECT"
If I put NULL where the ? is, then I get a hyphen (-) in my query result. If I omit the Else statement, I also get a hyphen in the query result. ' ' doesn't work at all. Does anyone have any thoughts?
Typically the way nulls are displayed is controlled by the client software used to display query results. If you insist on doing that in SQL, you will need to convert the date to a character string:
CASE
WHEN B.DTE_LNP_LAST > A.DTE_PROC_ACT
THEN VARCHAR_FORMAT(B.DTE_LNP_LAST)
ELSE ''
END AS "DATE OF DISCONNECT"
Replace VARCHAR_FORMAT() with the formatting function available in your DB2 version on your platform, if necessary.
You can use the coalesce function
Coalesce (column, 'text')
If the first value is null, it will be replaced by the second one.

Conditionally branching in SQL based on the type of a variable

I'm selecting a value out of a table that can either be an integer or a nvarchar. It's stored as nvarchar. I want to conditionally call a function that will convert this value if it is an integer (that is, if it can be converted into an integer), otherwise I want to select the nvarchar with no conversion.
This is hitting a SQL Server 2005 database.
select case
when T.Value (is integer) then SomeConversionFunction(T.Value)
else T.Value
end as SomeAlias
from SomeTable T
Note that it is the "(is integer)" part that I'm having trouble with. Thanks in advance.
UPDATE
Check the comment on Ian's answer. It explains the why and the what a little better. Thanks to everyone for their thoughts.
select case
when ISNUMERIC(T.Value) then T.Value
else SomeConversionFunction(T.Value)
end as SomeAlias
Also, have you considered using the sql_variant data type?
The result set can only have one type associated with it for each column, you will get an error if the first row converts to an integer and there are strings that follow:
Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the nvarchar value 'word' to data type int.
try this to see:
create table testing
(
strangevalue nvarchar(10)
)
insert into testing values (1)
insert into testing values ('word')
select * from testing
select
case
when ISNUMERIC(strangevalue)=1 THEN CONVERT(int,strangevalue)
ELSE strangevalue
END
FROM testing
best bet is to return two columns:
select
case
when ISNUMERIC(strangevalue)=1 THEN CONVERT(int,strangevalue)
ELSE NULL
END AS StrangvalueINT
,case
when ISNUMERIC(strangevalue)=1 THEN NULL
ELSE strangevalue
END AS StrangvalueString
FROM testing
or your application can test for numeric and do your special processing.
You can't have a column that is sometimes an integer and sometimes a string. Return the string and check it using int.TryParse() in the client code.
ISNUMERIC. However, this accepts +, - and decimals so more work is needed.
However, you can't have the columns as both datatypes in one go: you'll need 2 columns.
I'd suggest that you deal with this in your client or use an ISNUMERIC replacement
IsNumeric will get you part of the way there. You can then add some further code to check whether it is an integer
for example:
select top 10
case
when isnumeric(mycolumn) = 1 then
case
when convert(int, mycolumn) = mycolumn then
'integer'
else
'number but not an integer'
end
else
'not a number'
end
from mytable
To clarify some other answers, your SQL statement can't return different data types in one column (it looks like the other answers are saying you can't store different data types in one column - yours are all strign represenations).
Therefore, if you use ISNUMERIC or another function, the value will be cast as a string in the table that is returned anyway if there are other strigns being selected.
If you are selecting only one value then it could return a string or a number, however your front end code will need to be able to return the different data types.
Just to add to some of the other comments about not being able to return different data types in the same column... Database columns should know what datatype they are holding. If they don't then that should be a BIG red flag that you have a design problem somewhere, which almost guarantees future headaches (like this one).