SQL remove the next letter/character after a character ^ - sql-server-2008

How to remove a character after the character ^ from a selected rows in table?
e.g.
TABLE Things
Boat
Do^2gs
Cat^fs
^KBear
Mi^&ce
D^Rice
RESULTS:
Boat
Dogs
Cats
Bear
Mice
Dice

select case when charindex('^', col) <> 0
then stuff(col, charindex('^', col), 2, '')
else col
end
-- to handle multiple ^ up to max of 4
select t.col,
r4.col
from Things t
cross apply
(
select col = case when charindex('^', col) <> 0
then stuff(col, charindex('^', col), 2, '')
else col
end
) r1
cross apply
(
select col = case when charindex('^', r1.col) <> 0
then stuff(r1.col, charindex('^', r1.col), 2, '')
else r1.col
end
) r2
cross apply
(
select col = case when charindex('^', r2.col) <> 0
then stuff(r2.col, charindex('^', r2.col), 2, '')
else r2.col
end
) r3
cross apply
(
select col = case when charindex('^', r3.col) <> 0
then stuff(r3.col, charindex('^', r3.col), 2, '')
else r3.col
end
) r4
-- UDF to remove the ^
create function remove_chr
(
#str varchar(100)
)
returns varchar(100)
as
begin
while charindex('^', #str) <> 0
begin
select #str = case
when charindex('^', #str) <> 0
then stuff(#str, charindex('^', #str), 2, '')
else #str
end
end
return #str
end

If you are using MySQL you could use:
SELECT col,
IF(INSTR(col,'^') > 0,CONCAT(LEFT(col,INSTR(col, '^')-1),
RIGHT(col,LENGTH(col) - INSTR(col, '^')-1)), col) AS result
FROM Things;
SqlFiddleDemo
And SQL Server equivalent:
SELECT col,
IIF(CHARINDEX('^',col) > 0,CONCAT(LEFT(col,CHARINDEX('^',col)-1),
RIGHT(col,LEN(col) - CHARINDEX('^',col)-1)), col) AS result
FROM Things
LiveDemo
SQL Server 2008:
SELECT col,
CASE WHEN CHARINDEX('^',col) > 0
THEN LEFT(col,CHARINDEX('^',col)-1) + RIGHT(col,LEN(col) - CHARINDEX('^',col)-1)
ELSE col
END AS result
FROM Things;
Keep in mind that it will work only if there is none or one occurence of ^.

Here is the solution which removes any number of occurrence of '^' .
I have created a function SQL server which is not used any loop or cursor.
CREATE FUNCTION [dbo].[FnReplaceChar](#pOriginalText VARCHAR(2000))
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE #vText VARCHAR(1000)
,#vXML XML
--Convert text as XML format
SELECT #vXML = '<Root><dtl><f>' + REPLACE(#pOriginalText,'^','</f></dtl><dtl><f>^')+'</f></dtl></Root>'
--Splits words started with '^' and combines after removing character starts with '^'
SET #vText = (
SELECT '' + ACT_TEXT
FROM
(
SELECT CASE WHEN CHARINDEX('^',DOC.COL.value('f[1]','VARCHAR(100)') ,0) > 0
THEN STUFF(DOC.COL.value('f[1]','VARCHAR(100)'),CHARINDEX('^',DOC.COL.value('f[1]','VARCHAR(100)') ,0),2,'')
ELSE DOC.COL.value('f[1]','VARCHAR(100)')
END AS ACT_TEXT
FROM #vXML.nodes('/Root/dtl') DOC(COL)
)T
FOR XML PATH('')
)
RETURN #vText
END
You can use this function in your select query
SELECT dbo.[FnReplaceChar](col_Name)
FROM [Things]

Related

SQL delete unnecessary characters from a row and parse a table into a new table

I have created the below code:
with t as (select *,
case
when `2kids`= '1' then '2kids' else'' end as new_2kids,
case
when `3kids`= '1' then '3kids' else'' end as new_3kids,
case
when kids= '1' then 'kids' else'' end as kids
from test.family)
select concat_ws('/',new_2kids, new_3kids, new_kids) as 'nc_kids'
from t;
If I run this query my output will be:
nc_kids
2kids/new_3kids/
2kids//
/new_3kids/new_kids
2kids/new_3kids/new_kids
How can I remove all the unnecessary '/' which not followed by character.
For example:
nc_kids
2kids/new_3kids
2kids
new_3kids/new_kids
2kids/new_3kids/new_kids
concat_ws() ignore nulls, so you can just turn the empty strings to null values at concatenation time:
select concat_ws('/',
nullif(new_2kids, ''),
nullif(new_3kids, ''),
nullif(new_kids, '')
) as nc_kids
from t;
Better yet, fix the case expressions so they produce null values instead of empty stings in the first place:
with t as (
select f.*,
case when `2kids`= 1 then '2kids' end as new_2kids,
case when `3kids`= 1 then '3kids' end as new_3kids,
case when kids = 1 then 'kids' end as kids
from test.family f
)
select concat_ws('/',new_2kids, new_3kids, new_kids) as nc_kids
from t;

Concatenating fields in select clause JOOQ

I'm working in a query using JOOQ and I'm trying to output a column as a
concatenation (space separated) of other fields extracted in the same query.
Getting into detail, with the next code I try to create a select statement with a column called fullAdress by grouping all the address lines contained in the address table. So, for each field, if it's not null or empty it will be concatenated to the result (actually no space is being added).
#Override
protected List<Field<?>> selectCustomFields() {
List<Field<?>> customSelect = new ArrayList<Field<?>>();
// Fields to use in the concatenation
Field<?> field1 = field("addr.AddressLine1"), field2 = field("addr.AddressLine2"),field3 = field("addr.AddressLine3"),
field4 = field("addr.AddressLine4"), field5 = field("addr.PostalCode"), field6 = field("addr.City"),
field7 = field("addr.State"), field8 = field("addr.County"), field9 = field("addr.Country");
// Create non null/empty conditions
Condition condLine1 = field1.isNotNull().and(field1.length().ne(0));
Condition condLine2 = field2.isNotNull().and(field2.length().ne(0));
Condition condLine3 = field3.isNotNull().and(field3.length().ne(0));
Condition condLine4 = field4.isNotNull().and(field4.length().ne(0));
Condition condLine5 = field5.isNotNull().and(field5.length().ne(0));
Condition condLine6 = field6.isNotNull().and(field6.length().ne(0));
Condition condLine7 = field7.isNotNull().and(field7.length().ne(0));
Condition condLine8 = field8.isNotNull().and(field8.length().ne(0));
Condition condLine9 = field9.isNotNull().and(field9.length().ne(0));
// Concat address lines when meets condition
customSelect.add(concat(DSL.when(condLine1, field1),
DSL.when(condLine2, field2),
DSL.when(condLine3, field3),
DSL.when(condLine4, field4),
DSL.when(condLine5, field5),
DSL.when(condLine6, field6),
DSL.when(condLine7, field7),
DSL.when(condLine8, field8),
DSL.when(condLine9, field9))
.as("fullAddress"));
return customSelect;
}
JOOQ will generate the next from the previous select statement, which is giving a null value and not concatenating the fields correctly.
select
concat(
cast(case when (
addr.AddressLine1 is not null
and char_length(cast(addr.AddressLine1 as char)) <> 0
) then addr.AddressLine1 end as char),
cast(case when (
addr.AddressLine2 is not null
and char_length(cast(addr.AddressLine2 as char)) <> 0
) then addr.AddressLine2 end as char),
cast(case when (
addr.AddressLine3 is not null
and char_length(cast(addr.AddressLine3 as char)) <> 0
) then addr.AddressLine3 end as char),
cast(case when (
addr.AddressLine4 is not null
and char_length(cast(addr.AddressLine4 as char)) <> 0
) then addr.AddressLine4 end as char),
cast(case when (
addr.PostalCode is not null
and char_length(cast(addr.PostalCode as char)) <> 0
) then addr.PostalCode end as char),
cast(case when (
addr.City is not null
and char_length(cast(addr.City as char)) <> 0
) then addr.City end as char),
cast(case when (
addr.State is not null
and char_length(cast(addr.State as char)) <> 0
) then addr.State end as char),
cast(case when (
addr.County is not null
and char_length(cast(addr.County as char)) <> 0
) then addr.County end as char),
cast(case when (
addr.Country is not null
and char_length(cast(addr.Country as char)) <> 0
) then addr.Country end as char)) as `fullAddress`
from Address as `addr`
....
My questions are,
how should I create my select statement correctly?
how can I best add the space separator?
is there any better alternative to JOOQ ( when = case ) condition clause?
how should I create my select statement correctly?
You forgot the CASE .. ELSE part, or otherwise() in jOOQ:
// Concat address lines when meets condition
customSelect.add(concat(DSL.when(condLine1, field1).otherwise(""),
DSL.when(condLine2, field2).otherwise(""),
DSL.when(condLine3, field3).otherwise(""),
DSL.when(condLine4, field4).otherwise(""),
DSL.when(condLine5, field5).otherwise(""),
DSL.when(condLine6, field6).otherwise(""),
DSL.when(condLine7, field7).otherwise(""),
DSL.when(condLine8, field8).otherwise(""),
DSL.when(condLine9, field9).otherwise(""))
.as("fullAddress"));
how can I best add the space separator?
If you want an additional space separator between your address parts, you could write:
// Concat address lines when meets condition
customSelect.add(concat(DSL.when(condLine1, field1.concat(" ")).otherwise(""),
DSL.when(condLine2, field2.concat(" ")).otherwise(""),
DSL.when(condLine3, field3.concat(" ")).otherwise(""),
DSL.when(condLine4, field4.concat(" ")).otherwise(""),
DSL.when(condLine5, field5.concat(" ")).otherwise(""),
DSL.when(condLine6, field6.concat(" ")).otherwise(""),
DSL.when(condLine7, field7.concat(" ")).otherwise(""),
DSL.when(condLine8, field8.concat(" ")).otherwise(""),
DSL.when(condLine9, field9.concat(" ")).otherwise("")).trim()
.as("fullAddress"));
is there any better alternative to JOOQ ( when = case ) condition clause?
I think the approach is sound. Of course, you probably shouldn't repeat all that logic all the time, but create a loop of the sort:
List<Field<String>> list = new ArrayList<>();
for (int i = 0; i < fields.size(); i++) {
list.add(DSL.when(conditions.get(i), (Field) fields.get(i)).otherwise(""));
}
customSelect.add(concat(list.toArray(new Field[0])).trim().as("fullAddress"));

Using two lookup functions for one DIM KEY

I have created three lookup functions for three scenarios that can occur within the data. Each of these functions should return a account_dim_key, the first scenario takes the sr account ID and call data-time from a call table and returns a dim key if the call date-time is between effective start and end dates, the second scenario uses call date(no time) and the third scnario take just uses account id (no date). If the first lookup function returns a -1 then I want to use the second lookup function, and if that returns a -1 then I was to use the third lookup function. I have been trying to use them in a case statement but in all the scenarios I have tried it always returns the dim key from the second lookup function (in the THEN part of my case statement). Is it not possible to use three functions in a case statement this way or am I going about it the complete wrong way.
lookup 1
CREATE OR REPLACE FUNCTION LOOKUP_D_ACCT(p_acct_id IN VARCHAR2, p_date IN DATE)
RETURN NUMBER
PARALLEL_ENABLE
IS
v_dim_id NUMBER;
BEGIN
SELECT ACCOUNT_DIM_KEY
INTO v_dim_id
FROM
schema.D_ACCOUNT ac
WHERE p_acct_id = ac.ID
AND (p_date BETWEEN ac.EFFECTIVE_START_DT AND ac.EFFECTIVE_END_DT)
;
RETURN v_dim_id;
EXCEPTION
WHEN OTHERS
THEN
RETURN -1;
END LOOKUP_D_ACCT;
/
Lookup 2
CREATE OR REPLACE FUNCTION LOOKUP_D_ACCT_TRUNC_DT (p_acct_id IN VARCHAR2, p_date IN DATE)
RETURN NUMBER
PARALLEL_ENABLE
IS
v_dim_id NUMBER;
BEGIN
SELECT MAX(ACCOUNT_DIM_KEY)
INTO v_dim_id
FROM
schema.D_ACCOUNT ac
WHERE p_acct_id = ac.ID
AND p_date >= TRUNC(ac.EFFECTIVE_START_DT) AND p_date <= TRUNC(ac.EFFECTIVE_END_DT)
;
RETURN v_dim_id;
EXCEPTION
WHEN OTHERS
THEN
RETURN -1;
END;
/
Lookup 3
CREATE OR REPLACE FUNCTION LOOKUP_D_ACCT_NO_DT (p_acct_id IN VARCHAR2)
--LOOKUP FUNCTION TO PASS THROUGH FIELD_ALIGN_DIM_ID WITH DATE CRITERIA
--new one for practice load
--dated 8/31
RETURN NUMBER
PARALLEL_ENABLE
IS
v_dim_id NUMBER;
BEGIN
SELECT ACCOUNT_DIM_KEY
INTO v_dim_id
FROM
schema.D_ACCOUNT ac
WHERE p_acct_id = ac.ID
;
RETURN v_dim_id;
EXCEPTION
WHEN OTHERS
THEN
RETURN -1;
END;
/
The select of my procedure looks like this:
SELECT cl.id,
cl.account_vod__c,
cl.call_date_vod__c,
cl.call_datetime_vod__c,
(CASE
WHEN NVL (
LOOKUP_D_ACCT(cl.ACCOUNT_VOD__C,
CL.CALL_DATETIME_VOD__C),
-1) = -1
--lookup 1
THEN
NVL (
LOOKUP_ACCT_TRUNC_DT (cl.ACCOUNT_VOD__C,
CL.CALL_DATE_VOD__C),
-1)
--lookup 2
WHEN NVL (
LOOKUP_ACCT_TRUNC_DT (cl.ACCOUNT_VOD__C,
CL.CALL_DATE_VOD__C),
-1) = -1
THEN
NVL (LOOKUP_D_ACCT_NO_DT (cl.ACCOUNT_VOD__C), -1)
--lookup 3
ELSE -3
END)
AS ACCOUNT_DIM_KEY,
LOOKUP_D_ACCT_VEEVA (cl.ACCOUNT_VOD__C, CL.CALL_DATETIME_VOD__C)
AS DTIME_DIM_KEY,
--lookup 1
LOOKUP_ACCOUNT_DIM_TEST (cl.ACCOUNT_VOD__C, CL.CALL_DATE_VOD__C)
AS DT_DIM_KEY,
--lookup 2
LOOKUP_D_ACCT_VEEVA_NO_DT (cl.ACCOUNT_VOD__C) AS NODT_DIM_KEY
--lookup 3
FROM schema.CALLTABLE CL
WHERE CL.ID IN ('a043600000Bija3AAB')
I would expect the 'ACCOUNT_DIM_KEY' field to always be populated with the functions from lookup 1, 2, or 3
enter image description here
Any assistance would be greatly appreciated!
if you want to pass all the chain of your conditions, then you shouldn't use case statement, but in series check each condition:
if (NVL (LOOKUP_D_ACCT(cl.ACCOUNT_VOD__C, CL.CALL_DATETIME_VOD__C), -1) = -1) then
if (NVL ( LOOKUP_ACCT_TRUNC_DT (cl.ACCOUNT_VOD__C, CL.CALL_DATE_VOD__C), -1) = -1 ) then
if (NVL (LOOKUP_D_ACCT_NO_DT (cl.ACCOUNT_VOD__C), -1) = -1) then
-- if third func returned -1
else
-- action if third case
end if;
else
--action if second case
end if;
else
--action if first case
end if;
UPD
Sure, if it's a SQL not a PL/SQL, than you should use nested case, as has been said:
SELECT
(CASE
WHEN NVL (
LOOKUP_D_ACCT(cl.ACCOUNT_VOD__C,
CL.CALL_DATETIME_VOD__C),
-1) = -1
--lookup 1
THEN
CASE
WHEN
NVL (
LOOKUP_ACCT_TRUNC_DT (cl.ACCOUNT_VOD__C,
CL.CALL_DATE_VOD__C),
-1)= -1
THEN
NVL (LOOKUP_D_ACCT_NO_DT (cl.ACCOUNT_VOD__C), -1)
ELSE -3
END
WHEN NVL (
LOOKUP_ACCT_TRUNC_DT (cl.ACCOUNT_VOD__C,
CL.CALL_DATE_VOD__C),
-1) = -1
THEN
NVL (LOOKUP_D_ACCT_NO_DT (cl.ACCOUNT_VOD__C), -1)
--lookup 3
ELSE -3
END)
AS ACCOUNT_DIM_KEY,
LOOKUP_D_ACCT_VEEVA (cl.ACCOUNT_VOD__C, CL.CALL_DATETIME_VOD__C)
AS DTIME_DIM_KEY,
--lookup 1
LOOKUP_ACCOUNT_DIM_TEST (cl.ACCOUNT_VOD__C, CL.CALL_DATE_VOD__C)
AS DT_DIM_KEY,
--lookup 2
LOOKUP_D_ACCT_VEEVA_NO_DT (cl.ACCOUNT_VOD__C) AS NODT_DIM_KEY
--lookup 3
FROM schema.CALLTABLE CL
WHERE CL.ID IN ('a043600000Bija3AAB')

Format the SQL query to a variable

DECLARE #sql nvarchar(4000)
SET #sql='SELECT DISTINCT
WS.SIMNumber,
SMTP.SMTPMappingId,
CONVERT(VARCHAR(11),WS.ExpiryDate,106) as ExpiryDate,
CASE
WHEN BES.DisplayName IS NOT NULL THEN BES.DisplayName+'#'+SMTP.DomainName
ELSE
CASE WHEN BES.PIN IS NOT NULL THEN BES.PIN+'#'+SMTP.DomainName
ELSE '' END END AS EmailId,
CASE
WHEN (SELECT COUNT(*) FROM dbo.ExpiringEmailSimCardSent WHERE SimNumber=WS.SIMNumber AND ExpiryDate=WS.ExpiryDate)>0
THEN CONVERT(BIT,1)
ELSE CONVERT(BIT,0)
END AS IsEMailSent
FROM
WEBSERVICE_CACHE AS WS
LEFT OUTER JOIN
BES_SERVER_CACHE AS BES ON WS.SIMNumber = LEFT(BES.ICCID,19)
LEFT OUTER JOIN
BES_SMTP_Mapping AS SMTP ON BES.BESSqlServer = SMTP.BesServer
WHERE
CONVERT(DATETIME, GETDATE(), 109) > CONVERT(DATETIME, WS.ExpiryDate, 109)'
EXECUTE sp_executesql #sql
I have this SQL query want to convert it into a `nvarchar` variable because of # and '' . I am getting some errors
I am getting this errorMsg 102, Level 15, State 1, Line 9
Incorrect syntax near '#'.
If I rectify that it comes for another at # and ' '
You have to 'escape' your quotes:
This:
BES.PIN+'#'+SMTP.DomainName
Should be something like this:
BES.PIN+'''#'''+SMTP.DomainNam
experiment....

how to split a string column into 4 string columns?

I have definition column of a medication. I want the definition to be split into [product name(PN)], [doseform(DF)], [total dose(TD)] and [units].
Eg:
BENADRYL: MYLANTA 1:1 SOLUTION(Benadryl mylanta(PN),Sol(DF),1:1(TD),NULL(units))
MASK AND SPACER (Mark and Spacer(PN),NUll,NUll,NUll)
BL VITAMIN B-6 50 MG TABS(BL Vitamin(PN),Tabs(DF),50(TD),MG(Units))
I've made a few assumptions about your data. 1 is that the first opening bracket begins the definition ie. there are no brackets in the name before the definition. I am also assuming the the product definition structure will not change.
So, to get to your answer, I have used a modified split string function. I used this one as a base: T-SQL split string
The difference here is that the function will return a 2 column table so we can identify the definition parts
CREATE FUNCTION dbo.splitstring_custom
(
#stringToSplit VARCHAR(MAX)
)
RETURNS #returnList TABLE
(
[Type] [nvarchar](20)
,[Name] [nvarchar](500)
)
AS
BEGIN
DECLARE #name NVARCHAR(255)
DECLARE #pos INT
DECLARE #colidx INT = 1
WHILE CHARINDEX(',', #stringToSplit) > 0
BEGIN
SELECT #pos = CHARINDEX(',', #stringToSplit)
SELECT #name = SUBSTRING(#stringToSplit, 1, #pos - 1)
INSERT INTO #returnList
SELECT CASE WHEN #colidx = 1 THEN 'Product Name'
WHEN #colidx = 2 THEN 'Dose Form'
WHEN #colidx = 3 THEN 'Total Dose'
WHEN #colidx = 4 THEN 'Units'
END
,#name
SELECT #stringToSplit = SUBSTRING(#stringToSplit, #pos + 1, LEN(#stringToSplit) - #pos)
SET #colidx += 1
END
INSERT INTO #returnList
SELECT CASE WHEN #colidx = 1 THEN 'Product Name'
WHEN #colidx = 2 THEN 'Dose Form'
WHEN #colidx = 3 THEN 'Total Dose'
WHEN #colidx = 4 THEN 'Units'
END
,#stringToSplit
RETURN
END
Then we get the relevant part of the definition using some string manipulation:
SUBSTRING(medication, CHARINDEX('(', medication) + 1, LEN(medication) - (CHARINDEX('(', medication) + 1))
Then PIVOT the results to flatten it out:
DECLARE #t TABLE (id varchar(20), medication varchar(100))
INSERT INTO #t VALUES ('BENADRYL', 'BENADRYL: MYLANTA 1:1 SOLUTION(Benadryl mylanta(PN),Sol(DF),1:1(TD),NULL(units))')
INSERT INTO #t VALUES ('MASK', 'MASK AND SPACER (Mark and Spacer(PN),NULL,NULL,NULL)')
INSERT INTO #t VALUES ('BL VITAMIN', 'BL VITAMIN B-6 50 MG TABS(BL Vitamin(PN),Tabs(DF),50(TD),MG(Units))')
SELECT id, [Product Name], [Dose Form], [Total Dose], [Units]
FROM (SELECT id, Type, Name
FROM #t t
CROSS APPLY dbo.splitstring_custom(SUBSTRING(medication, CHARINDEX('(', medication) + 1, LEN(medication) - (CHARINDEX('(', medication) + 1)))
) X PIVOT ( MAX(Name) FOR TYPE IN ([Product Name], [Dose Form], [Total Dose], [Units]) ) pvt