Select dynamic string has a different value when referenced in Where clause - sql-server

I dynamically select a string built using another string. So, if string1='David Banner', then MyDynamicString should be 'DBanne'
Select
...
, Left(
left((select top 1 strval from dbo.SPLIT(string1,' ')) //first word
,1) //first character
+ (select top 1 strval from dbo.SPLIT(string1,' ')
//second word
where strval not in (select top 1 strval from dbo.SPLIT(string1,' ')))
,6) //1st character of 1st word, followed by up to 5 characters of second word
[MyDynamicString]
,...
From table1 Join table2 on table1pkey=table2fkey
Where MyDynamicString <> table2.someotherfield
I know table2.someotherfield is not equal to the dynamic string. However, when I replace MyDynamicString in the Where clause with the full left(left(etc.. function, it works as expected.
Can I not reference this string later in the query? Do I have to build it using the left(left(etc.. function each time in the where clause?

If you do it as you have it above, then the answer is yes, you have to recreate it again in the where clause.
As an alternative, you could use an inline view:
Select
...
, X.theString
,...
From table1 Join table2 on table1pkey=table2fkey
, (SELECT
string1
,Left(
left((select top 1 strval from dbo.SPLIT(string1,' ')) //first word
,1) //first character
+ (select top 1 strval from dbo.SPLIT(string1,' ')
//second word
where strval not in (select top 1 strval from dbo.SPLIT(string1,' ')))
,6) theString //1st character of 1st word, followed by up to 5 characters of second word
FROM table1
) X
Where X.theString <> table2.someotherfield
AND X.string1 = <whatever you need to join it to>

In SQL 2008 you can use the alias in the ORDER BY CLAUSE, but not in the where clause.
Why not wrap the calculation up into a user defined function (UDF) to avoid violating DRY and also to make the query more readable?
If it matters, here is an article that explains why you can't use a column alias in a HAVING, WHERE, or GROUP BY.

Related

SQL - CHARINDEX, ltrim rtim

I'm trying to understand what this query is doing (SQL Server):
Select StudentId, left(fullname,charindex(' ',fullname + ' ')) as mysteryCol
From (select StudentId, ltrim (rtrim (fullname)) as fullname from STUDENTS) as S
And the query:
Select top 10 msyteryCol , count(*) as howMany from
(Select left (fullname, charindex (' ', fullname + ' ')) as mysteryCol
From (select StudentId, ltrim (rtrim (fullname)) as fullname from STUDENTS) as S) as Z
Group by mysteryCol
Having count (*) > 100
Order by 2 desc
I only understood that the charindex will find the index place of an empty space ' ' from the fullname, but I haven't really understood what is the final output of this.
Thanks for all the helpers
Short answer : It will read the first name From a full name. The second query will just group based on first name and give first 10 names and count ordered in descending order that have a occurrence of more than 100.
Explanation : From (select StudentId, ltrim (rtrim (fullname)) as fullname from STUDENTS this line of code removes any leading and trailing spaces in the fullname. The only spaces left in name are now left between the first and last names(If any).
charindex(' ',fullname + ' ') This line gets the index of the first space that occurs in the full name. If the full name is only made up of one string fullname + ' ' takes care of that. This gives us the index of the first space that occurs in the name.
left(fullname,charindex(' ',fullname + ' ')) gets the string value to the left of first occurrence of a space character. Hence the first name.
Select top 10 msyteryCol , count(*) as howMany from
(Select left (fullname, charindex (' ', fullname + ' ')) as mysteryCol
From (select StudentId, ltrim (rtrim (fullname)) as fullname from STUDENTS) as
S) as Z
Group by mysteryCol
Having count (*) > 100
Order by 2 desc
This query groups the query by First name and counts the number of occurrences of each first name. It displays the top 10 names that have the most counts and where count of occurrences is greater than 100.

Need help in using REGEXP_REPLACE WITH LISTAGG function

WITH CTE AS (
SELECT
ID,
X.A_NO::INT AS A_NO,
REGEXP_REPLACE(TEXT_COL,'\::abcd::0|:::xyza::',' ') as TEXT_COL
FROM table X
WHERE date = '2022-02-02'
AND T_ID IN('12345','56789')
ORDER BY A_NO
)
SELECT
ID,
LISTAGG (distinct TEXT_COL , ',') WITHIN GROUP (ORDER BY TEXT_COL) AS TEXT_COL_A
FROM CTE
GROUP BY ID;
When I run the above query, I'm getting results as mentioned below:
ID TEXT_COL_A
12345 ,abc_xyz_ecom_data
56789
The value for TEXT_COL_A for the second row is empty. I want to remove the comma in the first row and update the second row AS NULL in the result. Can anyone guide me how to achieve this?
The second problem can be tackled with IFNULL
SELECT 'a' as a, IFNULL(a,'a was null') as b, null as c, IFNULL(c,'c was null') as d;
gives:
A
B
C
D
a
a
c was null
The first PROBLEM to remove things are the ends use TRIM, but to have just the start OR end trim use LTRIM or RTRIM
SELECT ',,a,,' as a, trim(a,','), ltrim(a,','), rtrim(a,',');
A
TRIM(A,',')
LTRIM(A,',')
RTRIM(A,',')
,,a,,
a
a,,
,,a

I want to know How to Segregate Digits Chars and Special Chars In Sql server Using CTE?

I have a string which contains characters,special characters and numbers in it.
what i have to do this i have to seperate all characters into one column and special characters into to another column and numbers into another column using common table expression.it would be helpful for me if anyone provide the code by using common table expression for obtaining the required result.
The Code what I have Tried So far:
DECLARE #search VARCHAR(200)
SET #search='123%#'
;with cte (Num,indexing) as (
SELECT
#search,0
UNION ALL
SELECT
indexing
num
from
cte
cross apply
(select indexing+1) C(NewLevel)
CROSS APPLY
(SELECT REPLACE(Num, NewLevel, '')) C2(NewInput)
WHERE
indexing<=LEN(NUM)
)
select Num
from cte
My input Should be:abcd12345$###
my Expected Output is:
Column 1 Column 2 Column 3
12345 $#### abcd
I would, personally, use a Tally to split the string into separate characters, and then use conditional string aggregation to create the new strings:
DECLARE #YourString varchar(200) = 'abcd12345$###';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP(LEN(#YourString))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3, N N4),
Chars AS(
SELECT V.YourString,
SS.C,
T.I,
CASE WHEN SS.C LIKE '[A-z]' THEN 'Letter'
WHEN SS.C LIKE '[0-9]' THEN 'Number'
ELSE 'Special'
END AS CharType
FROM (VALUES(#YourString)) V(YourString)
CROSS JOIN Tally T
CROSS APPLY (VALUES(SUBSTRING(V.YourString,T.I,1)))SS(C))
SELECT STRING_AGG(CASE CharType WHEN 'Letter' THEN C END,'') WITHIN GROUP (ORDER BY I) AS Letters,
STRING_AGG(CASE CharType WHEN 'Number' THEN C END,'') WITHIN GROUP (ORDER BY I) AS Numbers,
STRING_AGG(CASE CharType WHEN 'Special' THEN C END,'') WITHIN GROUP (ORDER BY I) AS Special
FROM Chars
GROUP BY YourString;
db<>fiddle
Note, as well, the use of whitespace, so yo can clearly see where one CTE starts and another begins.
Explanation
The first CTE (N), is just 10 NULL values. I use 10 as multiples of 10 are easy to work with.
Then next CTE (Tally) creates a Tally table. It Cross joins N to itself 4 times, creating 10^4 rows, or 10,000 rows, each with an incrementing number, due to ROW_NUMBER. We don't need all of those rows, however, so I limit it to the length of your variable.
Then we get onto Chars; this splits the string into its individual characters, 1 row per character, numbers them and defines the type they are, a Letter, Number or Special Character.
Then we finally, after the CTEs, aggregate the values using a conditional STRING_AGG, so that only the character type you want is aggregated in the column.
If you "must" use a rCTE, you can do it this way, but, like I have mentioned, a rCTE is a far slower solution and suffers from maximum recursion errors (If you have more than 100 characters, you'll need to change the MAXRECURSION value in the OPTION clause):
DECLARE #YourString varchar(200) = 'abcd12345$###';
WITH Chars AS(
SELECT V.YourString,
1 AS I,
SS.C,
CASE WHEN SS.C LIKE '[A-z]' THEN 'Letter'
WHEN SS.C LIKE '[0-9]' THEN 'Number'
ELSE 'Special'
END AS CharType
FROM (VALUES(#YourString))V(YourString)
CROSS APPLY (VALUES(SUBSTRING(#YourString,1,1)))SS(C)
UNION ALL
SELECT C.YourString,
C.I + 1 AS I,
SS.C,
CASE WHEN SS.C LIKE '[A-z]' THEN 'Letter'
WHEN SS.C LIKE '[0-9]' THEN 'Number'
ELSE 'Special'
END AS CharType
FROM Chars C
CROSS APPLY (VALUES(SUBSTRING(#YourString,C.I + 1,1)))SS(C)
WHERE C.I + 1 <= LEN(C.YourString))
SELECT STRING_AGG(CASE CharType WHEN 'Letter' THEN C END,'') WITHIN GROUP (ORDER BY I) AS Letters,
STRING_AGG(CASE CharType WHEN 'Number' THEN C END,'') WITHIN GROUP (ORDER BY I) AS Numbers,
STRING_AGG(CASE CharType WHEN 'Special' THEN C END,'') WITHIN GROUP (ORDER BY I) AS Special
FROM Chars
GROUP BY YourString;
db<>fiddle

SQL replace occurrances based on a table

Hi I've a SQL issue to solve; I've these tables:
Table A with varchar column tst
tst
'2','5','8'
'2','6'
'4','12'
Table B with int column rep
rep
2
6
I'm looking for a query (without cycle WHILE) to update Table A in the following way:
tst
'R','5','8'
'R','R'
'4','12'
using char 'R' to replace the occurrances of Table B in Table A
Thanks in advance
SQLFiddle Demo
UPDATE t1
SET tst = STUFF(z,1,1,'') --Remove leading comma from final result
FROM (
SELECT --Convert original string to xml
tst
,CAST('<a>'+REPLACE(tst ,',','</a><a>')+'</a>' AS XML) x
FROM tst
) t1
CROSS APPLY (
SELECT --Replace value with 'R' when matched in rep
','+CASE WHEN rep IS NULL THEN y.value('.','varchar(max)') ELSE '''R''' END
FROM x.nodes('a') t2(y) --Explode xml to separate values
LEFT JOIN rep t3 --Match value to rep
ON y.value('.','varchar(max)') = QUOTENAME(rep,CHAR(39))
FOR XML PATH('') --Recompact xml to comma-delimited string
) t4(z)
Got it working by using a recursive CTE:
;with numbers as (
SELECT
rep,
-- processing order
ROW_NUMBER() OVER (order by rep) working_order
FROM B
), worker as (
-- Anchor: the first substitution
SELECT
tst,
rep,
-- stores already done substitutions
replace(tst, '''' + cast(rep as varchar) + '''', '''R''') tmp_result,
1 lvl
FROM A JOIN numbers ON working_order=1
UNION ALL
-- run through all substitutions to be done
SELECT
w.tst,
n.rep,
-- use tmp_result to refer to already done substitutions
replace(w.tmp_result, '''' + cast(n.rep as varchar) + '''', '''R'''),
lvl + 1
FROM worker w JOIN numbers n ON working_order=lvl+1
), result as (
SELECT tst, tmp_result FROM worker where lvl = (SELECT MAX(working_order) FROM numbers)
)
UPDATE A SET tst=tmp_result FROM A JOIN result ON result.tst=A.tst
Explanation:
First I select all numbers from B and give them a processing number
In the recursive CTE worker, I do a sequential substitution with the
order given by numbers
In result I reduce the worker to the final
rows (those with the highest working_order)
Finally I update A using
the result.

SQL Server : use LIKE inside a CASE statement to replace characters in string

Apologies for poorly worded question.
I have the following column description with contents like:
2511420s5
38114CR
2510014IND
I want to get rid of the s5,CRand INDusing a CASE statement.
Something like:
select
CASE
WHEN description LIKE ('%CRT%', '%IND%', '%MR%') THEN ''
ELSE description
END as test
from table
So to summarize, I want to strip the S5 from my string if it exists.
I have a list of about 15 possibilities so don't want to use a nested replace statement.
Can this be done with CASE or is there a better option?
Thanks,
Assuming there is no better rule; i.e. the text is not always at the end of the string, you could store the ignore tokens in a table, join and examine;
;with ignore(token) as ( --a real table is better
select 's5' union
select 'ind' union
select 'cr'
)
select distinct
id, -- need > 1 col
case when ignore.token is null then description else '' end
from table
left join ignore on description like '%' + token + '%'
This is the correct query:
;with ignore(token) as (
select 's5' union
select 'ind' union
select 'cr'
)
select distinct
id, -- need > 1 col
case when ignore.token is null then description
else REPLACE(description, ignore.token, '')
end
from table_1
left join ignore on description like '%' + token + '%';

Resources