T-SQLpad leading zeros in string for values between "." character - sql

I want to convert numbers to 3 decimal places for each number between the character ".". For example:
1.1.5.2 -> 001.001.005.002
1.2 -> 001.002
4.0 -> 004.000
4.3 ->004.003
4.10 -> 004.010
This is my query:
SELECT ItemNo
FROM EstAsmTemp

This is fairly easy once you understand all the steps:
Split the string into the individual data points.
Convert the parsed values into the format you want.
Shove the new values back into a delimited list.
Ideally you shouldn't store data with multiple datapoints in a single intersection like this but sometimes you just have no choice.
I am using the string splitter from Jeff Moden and the community at Sql Server Central which can be found here. http://www.sqlservercentral.com/articles/Tally+Table/72993/. There are plenty of other decent string splitters out there. Here are some excellent examples of other options. http://sqlperformance.com/2012/07/t-sql-queries/split-strings.
Make sure you understand this code before you use it in your production system because it will be you that gets the phone call at 3am asking for it to be fixed.
with something(SomeValue) as
(
select '1.1.5.2' union all
select '1.2' union all
select '4.0' union all
select '4.3' union all
select '4.10'
)
, parsedValues as
(
select SomeValue
, right('000' + CAST(x.Item as varchar(3)), 3) as NewValue
, x.ItemNumber as SortOrder
from something s
cross apply dbo.DelimitedSplit8K(SomeValue, '.') x
)
select SomeValue
, STUFF((Select '.' + NewValue
from parsedValues pv2
where pv2.SomeValue = pv.SomeValue
order by pv2.SortOrder
FOR XML PATH('')), 1, 1, '') as Details
from parsedValues pv
group by pv.SomeValue

I decided to change it in the presentation layer, per Zohar Peled's comment.

You did not mention the number of '.' separator a column can have. I assume, the max is 4 and the solution is below.
SELECT STUFF(ISNULL('.' + RIGHT('000' + PARSENAME(STRVALUE,4),4),'') + ISNULL('.' + RIGHT('000' + PARSENAME(STRVALUE,3),4) ,'') + ISNULL('.' + RIGHT('000' + PARSENAME(STRVALUE,2),4) ,'') + ISNULL('.' + RIGHT('000' + PARSENAME(STRVALUE,1),4),''),1,1,'')
FROM (VALUES('1.1.5.2'), ('1.2'), ('4.0'),('4.3'), ('4.10')) A (STRVALUE)

Related

Need to add double quotes to SQL SERVER string values

I have integer values that are being passed from a parameter that needed to be input as a string and padded so that they are 7 digits. This will then be passed into another query.
declare #t table(ctl_num int)
insert into #t values(5675, 45464, 2323)
declare #control_num varchar(500)
set #control_num = (select stuff((select ',' + right('000' + cast(ctl_num as varchar),7)
from #t
FOR XML PATH('')),1, 1', ''))
This codes sets #control_num as ('0005675, 0045464, 0002323'). I need this to be passed as ("0005675", "0045464", "0002323").
I've looked at other examples on-line but I can't seem to get this to work. Does anyone know how to get the double quotes around each value?
I think there is some issue in setting #control_num.
Please try the following:
set #control_num = (select stuff((select ',"' + right('000' + cast(ctl_num as varchar),7) + '"'
from #t
FOR XML PATH('')),1, 1, ''))
I would suggest:
select string_agg(concat('"', right('000' + cast(ctl_num as varchar(255)), 7), '"', ',')
from #t;
string_agg() has been available since SQL Server 2017. Also note that you should always include a length when referring to strings in SQL Server.

SQL - Remove Duplicate value between two columns

I'm looking a simple way to remove an unwanted Duplicate value.
The Dupe is part of a reference to another column, and not within the column itself, but the column I want to remove the dupe value from is multi-delimited with other values.
Here is an example table:
ID,Thing
Dog,Cat;Dog;Bird
Snake,Horse;Fish;Snake
Car,Car;Bus;Bike
As you can see Dog,Snake,Car are the values I need to remove from the Thing column.
Output:
ID,Thing
Dog,Cat;Bird
Snake,Horse;Fish
Car,Bus;Bike
Is there a way to match within a multidelimited field and pull out the exact match?
I'm using SQL Server MGMT studio. Thanks.
WITH CTE AS
(
SELECT ID, Thing, ROW_NUMBER() OVER (PARTITION BY Thing) AS rn
)
DELETE
FROM CTE
WHERE rn > 1
I believe this will do it. Test first by running just the CTE part of the query so you can see what rn is.
Your question and sample data is not very clear. I think what you want is to remove anything from the second column that is in the first column, in which case you can try using replace
select Id,
replace(replace(thing,id,''),';;',';')
from table
Storing multi-value elements in a column is never a good idea and is a conflict of interest with the relational data model; it pretty much always causes problems at some point.
What you can do is concatenate a leading and a trailing ; to the value of Thing and then replace the value of ID with an empty string.
Then remove the leading and trailing ;.
If your version of SQL Server is 2017+, you can use the function TRIM():
SELECT Id,
TRIM(';' FROM REPLACE(';' + Thing + ';', ';' + ID + ';', ';')) Thing
from tablename;
For previous versions use SUBSTRING():
SELECT Id,
SUBSTRING(
REPLACE(';' + Thing + ';', ';' + ID + ';', ';'),
2,
LEN(REPLACE(';' + Thing + ';', ';' + ID + ';', ';')) - 2
) Thing
from tablename;
If you want to update the table:
UPDATE tablename
SET Thing = TRIM(';' FROM REPLACE(';' + Thing + ';', ';' + ID + ';', ';'));
or:
UPDATE tablename
SET Thing = SUBSTRING(
REPLACE(';' + Thing + ';', ';' + ID + ';', ';'),
2,
LEN(REPLACE(';' + Thing + ';', ';' + ID + ';', ';')) - 2
);
See the demo.
I don't really understand what "multi-delimited" means with respect to a string. In your context it seems to suggest that you might have different types of delimiters. It definitely does mean that you have a really poor data model. If you want to remove the id from the things column, then my first suggestion is to fix the delimiters.
In SQL Server, you could use:
select t.*,
(select string_agg(s.value, ';')
from string_split(replace(t.things, ',', ';'), ';') s
where s.value <> t.id
) as new_things
from t;
If the delimited have some intrinsic meaning (did I mentioned that you should fix the data model?), then you can use a more brute force approach. Here is one method:
select t.*,
(case when things = id then ''
when things like concat(id, '[,;]%')
then stuff(things, 1, len(id) + 1, '')
when things like concat('%[,;]', id)
then left(things, len(things) - len(id) - 1)
when things like concat('%[,;]', id, '[,;]%')
then stuff(things, patindex(concat('%[,;]', id, '[,;]%'), things), len(id) + 1, '')
else things
end)
from t;
Here is a db<>fiddle.
Your Question is a good one. I used simple case statement to get the answer. It is CHARINDEX that helped to find the location of the value in Id column and then identified the position of the value in id and according to the position, string was replaced by required values.
--Preparing the table
SELECT *
INTO t
FROM (VALUES
('Dog', 'Cat;Dog;Bird'),
('Snake', 'Horse;Fish;Snake'),
('Car', 'Car;Bus;Bike')
) v(id, things)
--Query
SELECT id
,CASE WHEN CHARINDEX(reverse(id), reverse(things), 1) = 1 THEN REPLACE(things,';'+id ,'')
WHEN CHARINDEX(id, things, 1) < LEN(things) AND CHARINDEX(id, things, 1) > 1 THEN REPLACE(things, id +';' ,'')
WHEN CHARINDEX(id, things, 1) = 1 THEN REPLACE(things, id +';' ,'')
ELSE 'End'
END as [things]
FROM t

Find out Equal row set

I have a number of row base on plan and plan detail, I want to find out the same row set with other detail like plan 1 has 3 rows of data in detail table so need to find out the same rows for another plan. I have some sample data with this post may be more helpful to understand my problem. below is the Sample data Image, iwan to group by the record but not on a single row base on the full row set base on
PlanId, MinCount, MaxCount and CurrencyId
my expected data is below
I had tried to do with Some lengthy process like append all data in a single row and compare with other data, but it seems very lengthy process and takes to much time for 100 records I have an approx 20000 records in an actual database so not a good solution, please suggest me some thought
This would be trivial in MS Sql Server 2017 by using STRING_AGG.
But in 2012 one of the methods is to use the FOR XML trick.
SELECT PlanId, StairCount, MinCount, MaxCount, CurrencyId,
STUFF((
SELECT CONCAT(',', t1.AccountId)
FROM YourTable t1
WHERE t1.PlanId = t.PlanId
AND t1.StairCount = t.StairCount
AND t1.MinCount = t.MinCount
AND t1.MaxCount = t.MaxCount
AND t1.CurrencyId = t.CurrencyId
ORDER BY t1.AccountId
FOR XML PATH('')), 1, 1, '') AS AccountIdList
FROM YourTable t
GROUP BY PlanId, StairCount, MinCount, MaxCount, CurrencyId
Test here
Your desired result is rather hard to produce in SQL Server. The simplest method requires two levels of string concatenation:
with t as (
select a.account_id,
stuff( (select '[' +
convert(varchar(255), staircount) + ',' +
convert(varchar(255), mincount) + ',' +
convert(varchar(255), maxcount) + ',' +
convert(varchar(255), currencyid) +
']'
from t
where t.account_id = a.account_id
order by staircount
for xml path ('')
), 1, 1, '') as details
from (select distinct account_id from t) a
)
select d.details,
stuff( (select cast(varchar(255), account_id) + ','
from t
where t.details = d.details
for xml path ('')
), 1, 1, '') as accounts
from (select distinct details from t) d;
This isn't exactly your output, but it might be good enough for your problem.

Split alpha and numeric using sql -followup Q

This is actually a follow-up question to a topic mentioned here:
split alpha and numeric using sql
Facing similar problems, I adjusted #knkarthick24 (a chance to say tnx!) query to my needs almost perfectly (I have “2,500” not “2 500”) as follow:
SELECT [Quantity]
,substring(replace(subsrtunit, ',', ''), PATINDEX('%[0-9.]%', replace(subsrtunit, ',', '')) + 1, len(subsrtunit)) AS unit
,LEFT(replace(subsrtnumeric, ',', ''), PATINDEX('%[^0-9.]%', replace(subsrtnumeric, ',', '') + 't') - 1) AS num
FROM (
SELECT [Quantity]
,subsrtunit = SUBSTRING([Quantity], posofchar, LEN([Quantity]))
,subsrtnumeric = SUBSTRING([Quantity], posofnumber, LEN([Quantity]))
FROM (
SELECT [Quantity]
,posofchar = PATINDEX('%[^0-9.]%', replace([Quantity], ',', ''))
,posofnumber = PATINDEX('%[0-9.]%', replace([Quantity], ',', ''))
FROM [OPI].[dbo].[MRRInvoices]
WHERE DocumentNum IS NOT NULL
) d
) t
the only thing left is handling negative values.
Right now the results I'm getting for a negative value in Quantity field (-1.00 GB) is (.00 GB) in the unit field and (1.00) for num field.
Also, does anyone know how to "translate" it to derived column in SSIS?
Can "Findstring" in SSIS replace PATINDEX?
Thank you all in advance.
!1[img]

SQL Server substring w/o custom functions

I have a dilemma regarding how to extract a sub string from a larger string without using custom functions
The string is of this form:
" [1].[2]"
" [1].[2].[3]"
" [1].[2].[3].[4]"
Basically, it's a hierarchy that has 4 leading spaces for every child of a node. The task is to maintain those 4 leading spaces for every node, but get only the child in the end, without the full path
So, the final result should be something like this:
" [2]"
" [3]"
" [4]"
Could anyone help me?
Edit: or, if it's the case, should I modify the CTE that is building the result set?
The CTE is like the one below:
WITH Hierarchy(cid, cname, parent_id, Parents, nname)
AS
(
SELECT map_id, name, parent_id, CAST('' AS VARCHAR(MAX)), CAST(''+name AS VARCHAR(MAX))
FROM tblMapping AS tm1
WHERE parent_id = 0
UNION ALL
SELECT tm.map_id, tm.name, Parent.cid,
CAST(CASE WHEN Parent.Parents = ''
THEN(CAST(tm.parent_id AS VARCHAR(MAX)))
ELSE(Parent.Parents + '.' + CAST(tm.parent_id AS VARCHAR(MAX)))
END AS VARCHAR(MAX)),
-- add the formated column description
CAST(CASE WHEN Parent.Parents = ''
THEN(Parent.cname)
ELSE(' '+Parent.nname+ '.'+tm.name)
END AS VARCHAR(MAX))
FROM tblMapping AS tm
INNER JOIN Hierarchy AS Parent ON tm.parent_id = Parent.cid
)
SELECT *
FROM Hierarchy
ORDER BY ltrim(nname)
OPTION(MAXRECURSION 32767)
Thank you!
Try something like this
UPDATED: Inlcudes a root node now...
DECLARE #mockup TABLE(YourString VARCHAR(100));
INSERT INTO #mockup VALUES
(' [1]')
,(' [1].[2]')
,(' [1].[2].[3]')
,(' [1].[2].[3].[4]');
SELECT LEFT(YourString,CHARINDEX('[',YourString)-1)
+SUBSTRING(YourString,LEN(YourString)+1-CHARINDEX('[',REVERSE(YourString)),1000)
FROM #mockup;
I would concatenate the spaces part with the last index.
Something like
Select left(your_string, char_index(your_string, "[") - 1) +
right(your_string, 3) from your_table
The 3 is assuming there's always one digit. If not, this is a bit complex because no last index of in SQLServer:
Select left(your_string, char_index(your_string, "[") - 1) +
reverse(left(reverse(your_string), char_index(reverse(your_string), "[")))
from your_table