How to find out the length of an expression within a column in SQL Server 2008 - sql

I am not a database administrator but I often have to query databases to do my job. Recently I've been tasked with querying for the length of a semi-colon separated expression within a database column. It will probably be easier to show as a made-up example table and column:
Table is Table1.
Column is Column1.
Values of two rows in Table1.Column1 look like this:
principal_name='Joe Schmoe'; marital_status='m'; shoe_size='12.5';
message='This is a message which is 45 characters long';
years_active='15'
principal_name='Jim Schmim'; marital_status='s'; shoe_size='10.5';
message='This is a xxxxxxxxxxx message which is 57 characters long';
years_active='6'
I need to query Table1.Column1 and find out out how many rows have more than 50 characters in the message portion of this column.
If this column only had a single value, I could just use something like:
SELECT COUNT(*) FROM Table1 WHERE LEN(column1) > 40
However, I don't need to know how many total characters the field has, only how many are between message= and ;
I have never dealt with a column that has multiple values separated by semicolons before, so I don't know how to query for what I need to know.

Assuming there's always the same parts within column1, in the same order, something like
where ( CharIndex('years_active=',column1) - CharIndex('message=',column1) ) >50
(give or take some adjustments for lengths of descriptions, etc)

You can try something like this in a custom function
Declare #str varchar(max);
set #str = 'aaaabc=thisisatest;aaaaa'
select LEN(substring(#str,CHARINDEX('=',#str,0)+1, CHARINDEX(';',#str,0)-CHARINDEX('=',#str,0)-1))

Try this
;with cte as
(
select 'principal_name=''Joe Schmoe''; marital_status=''m''; shoe_size=''12.5'';message=''This is a message which is 45 characters long'';years_active=''15''' as column1
union
select 'principal_name=''Jim Schmim''; marital_status=''s''; shoe_size=''10.5''; message=''This is a xxxxxxxxxxx message which is 57 characters long''; years_active=''6'''
),
cte2 as
(
SELECT ltrim(rtrim(r.value('.','VARCHAR(MAX)'))) as Item from (
select CONVERT(XML, N'<root><r>' + REPLACE(column1,';','</r><r>') + '</r></root>') as XmlString
from cte ) x
CROSS APPLY x.XmlString.nodes('//root/r') AS RECORDS(r)
)
--SELECT *, LEN(item) - 10 from cte2 x where x.Item like 'message=%' and LEN(item) > 50
SELECT COUNT(*) cnt from cte2 x where x.Item like 'message=%' and LEN(item) > 50

Related

How to fetch only a part of string

I have a column which has inconsistent data. The column named ID and it can have values such as
0897546321
ABC,0876455321
ABC,XYZ,0873647773
ABC,
99756
test only
The SQL query should fetch only Ids which are of 10 digit in length, should begin with a 08 , should be not null and should not contain all characters. And for those values, which have both digits and characters such as ABC,XYZ,0873647773, it should only fetch the 0873647773 . In these kind of values, nothing is fixed, in place of ABC, XYZ , it can be anything and can be of any length.
The column Id is of varchar type.
My try: I tried the following query
select id
from table
where id is not null
and id not like '%[^0-9]%'
and id like '[08]%[0-9]'
and len(id)=10
I am still not sure how should I deal with values like ABC,XYZ,0873647773
P.S - I have no control over the database. I can't change its values.
SQL Server generally has poor support regular expressions, but in this case a judicious use of PATINDEX is viable:
SELECT SUBSTRING(id, PATINDEX('%,08[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9],%', ',' + id + ','), 10) AS number
FROM yourTable
WHERE ',' + id + ',' LIKE '%,08[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9],%';
Demo
If you normalise your data, and split the delimited data into parts, you can achieve this some what more easily:
SELECT SS.value
FROM dbo.YourTable YT
CROSS APPLY STRING_SPLIT(YT.YourColumn,',') SS
WHERE LEN(SS.value) = 10
AND SS.value NOT LIKE '%[^0-9]%';
If you're on an older version of SQL Server, you'll have to use an alternative String Splitter method (such as a XML splitter or user defined inline table-value function); there are plenty of examples on these already on Stack Overflow.
db<>fiddle

SQL Server 2017 - Concatenate values based on order number

I am doing concatenation of values where there is grouping value greater than 1. This works fine, but now I am trying to figure out how to utilize an sequence/order number in which to concatenate the values in that order. Is there a way I can do this?
So for example, I have a table which has the following:
I need the ability to concatenate Field1 and Field4 since the StagingCol is the same name and I also need to be able to concatenate in the order provided in the ConcatenateOrder column. I can't have it out of sequence i.e. Field4 + Field1
This is a snippet of the code I have so far which is working fine, it concatenates the two LandingZoneCol values...
--DECLARATION OF LANDING ZONE FIELD NAMES CONCATENATED TOGETHER AND DELMITED BY A COMMA WHERE VALUE NEEDS TO BE CONCATENATED (I.E. SUBSCRIBER + SEQ# = MEMBER_ID)
SELECT #ConcatLandingZoneFieldNames = ISNULL(#ConcatLandingZoneFieldNames,'') + ISNULL(LandZoneMapping.LandingZoneFieldName,'') + ', '
FROM #LandingZoneMapping AS LandZoneMapping
WHERE LandZoneMapping.StagingColumnName <> 'BreadCrumb'
AND LandZoneMapping.StagingColumnName IN (SELECT StagingColumnName
FROM #TEST
WHERE Total > 1)
--DECLARATION OF VARIABLES
SET #ConcatLandingZoneFieldNames = CONCAT('CONCAT(',SUBSTRING(#ConcatLandingZoneFieldNames,1,LEN(#ConcatLandingZoneFieldNames)-1),')')
Current Results
CONCAT(Field1, Field4)
Expected Results
CONCAT(Field1, Field4)
Although both Current and Expected are the same right now, I want to ensure that the concatenation of the values are concatenated in the correct order. If I was to flip the ConcatenateOrder numbers in the above table then the outcome would be different. The "Current" Results would end up being CONCAT(Field1, Field4) but the "Expected Results" should be CONCAT(Field4, Field1)
Any help would be greatly appreciated.
Your code looks like SQL Server. You an use string_agg():
select string_agg(lzm.landingzonecol, ',') within group (order by lzm.concatenateorder)
from #LandingZoneMapping lzm
where lzm.stagingcol = 'ID';
You can control the ordering with the order by clause.
As mention by Gordon you can use string_agg (Doc) function if you are using SQL Server version 2017 or above.
In case if you need same functionality for SQL Server version below 2017, use this:
SELECT STUFF((
SELECT CONCAT (
','
,LandingZoneCol
)
FROM #LandingZoneMapping LZM
WHERE StagingCol = 'ID'
ORDER BY ConcatenateOrder
FOR XML PATH('')
), 1, 1, '') AS Result

Insert into sql Max +1 only numbers (prevent characters)

I'm using this code
(SELECT (MAX(CODE) +1 WHERE ISNUMERIC([code]) = 1)
I want to max +1 only my numbers of my column preventing characters characters.
NOTE: THIS QUESTION WAS TAGGED MYSQL WHEN THIS ANSWER WAS POSTED.
You can use substring_index() to split the values and then re-unite them:
(SELECT CONCAT(SUBSTRING_INDEX(MAX(Code), '-', 1), '-',
SUBSTRING_INDEX(MAX(CODE), '-', -1) + 1
)
FROM . . .
WHERE code LIKE '%NEW-1%'
)
This assumes that the wildcards do not have hyphens in them, and that the values after the "1" are all numbers.
Also, this doesn't pad the number is zeroes, but that is a good idea for such codes -- it ensures that they are always the same length and that they sort correctly.
The MAX() function accepts expressions, not just column names:
SELECT MAX(CASE ISNUMERIC(code) WHEN 1 THEN code END)+1 as next_code
FROM (
SELECT '15' AS code
UNION ALL SELECT ' 98 ' AS code
UNION ALL SELECT 'New-45' AS code
) foo
WHERE ISNUMERIC(code)=1;
16
(Link is to SQL Server 2005, docs for SQL Server 2000 are apparently no longer on line, but MAX() belongs to SQL standard anyway.)

Selecting varchar values from a column

I'm working on SQL 2008 and have a table with 1000's of codes in it - a sample would be:
D37
D37.0
D38
D38.0
D39
D39.0
D3A
D3A.0
D40
What I need to do is select the values between D37 and D40. However, I do not want the D3A values (or D3B or D3C for that matter). I have tried the following:
SELECT Code
FROM Table
WHERE Code BETWEEN 'D37' AND 'D40'
However, this get all the codes listed above, including the D3A codes.
Is there a way to exclude the codes that do not fall in the 37-40 range?
Assuming that the single-letter convention is followed throughout, and there aren't any more weird characters than shown in the data, you can do this:
WITH cte AS (
[MyColumn]
, SELECT SUBSTRING([MyColumn],2,LEN([MyColumn)-1) AS Code
FROM MyTable
WHERE ISNUMERIC(SUBSTRING([MyColumn],2,LEN([MyColumn)-1)=1
)
SELECT [MyColumn]
FROM cte
WHERE CAST(Code AS float) BETWEEN 37 AND 40
It's messy, but this should do what you are asking.
SELECT code
from Mytable
where
ISNUMERIC( SUBSTRING(code, 2, (len(code)) ) ) > 0
and convert(float, SUBSTRING(code, 2, (len(code))) ) between 37 and 40

SQL Server - Select column that contains query string and split values into anothers 'columns'

I need to do a select in a column that contains a query string like:
user_id=300&company_id=201503&status=WAITING OPERATION&count=1
I want to perform a select and break each value in a new column, something like:
user_id | company_id | status | count
300 | 201503 | WAITING OPERATION | 1
How can i do it in SQL Server without use procs?
I've tried a function:
CREATE FUNCTION [xpto].[SplitGriswold]
(
#List NVARCHAR(MAX),
#Delim1 NCHAR(1),
#Delim2 NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
SELECT
Val1 = PARSENAME(Value,2),
Val2 = PARSENAME(Value,1)
FROM
(
SELECT REPLACE(Value, #Delim2, '&') FROM
(
SELECT LTRIM(RTRIM(SUBSTRING(#List, [Number],
CHARINDEX(#Delim1, #List + #Delim1, [Number]) - [Number])))
FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_objects) AS x
WHERE Number <= LEN(#List)
AND SUBSTRING(#Delim1 + #List, [Number], LEN(#Delim1)) = #Delim1
) AS y(Value)
) AS z(Value)
);
GO
Execution:
select QueryString
from User.Log
CROSS APPLY notifier.SplitGriswold(REPLACE(QueryString, ' ', N'ŏ'), N'ŏ', '&') AS t;
But it returns me only one column with all inside:
QueryString
user_id=300&company_id=201503&status=WAITING OPERATION&count=1
Thanks in advance.
I've had to do this many times before, and you're in luck! Since you only have 3 delimiters per string, and that number is fixed, you can use SQL Server's PARSENAME function to do it. That's far less ugly than the best alternative (using the XML parsing stuff). Try this (untested) query (replace TABLE_NAME and COLUMN_NAME with the appropriate names):
SELECT
PARSENAME(REPLACE(COLUMN_NAME,'&','.'),1) AS 'User',
PARSENAME(REPLACE(COLUMN_NAME,'&','.'),2) AS 'Company_ID',
PARSENAME(REPLACE(COLUMN_NAME,'&','.'),3) AS 'Status',
PARSENAME(REPLACE(COLUMN_NAME,'&','.'),4) AS 'Count',
FROM TABLE_NAME
That'll get you the results in the form "user_id=300", which is far and away the hard part of what you want. I'll leave it to you to do the easy part (drop the stuff before the "=" sign).
NOTE: I can't remember if PARSENAME will freak out over the illegal name character (the "=" sign). If it does, simply nest another REPLACE in there to turn it into something else, like an underscore.
You need to use SQL SUBSTRING as part of your select statement. You would first need to build the first row, then use a UNION to return the second row.