Adding hyphen in a column in sql table - sql

I need help understanding how to add a hyphen to a column where the values are as follows,
8601881, 9700800,2170
The hyphen is supposed to be just before the last digit. There are multiple such values in the column and the length of numbers could be 5,6 or more but the hyphen has to be before the last digit.
Any help is greatly appreciated.
The expected output should be as follows,
860188-1,970080-0,217-0

select concat(substring(value, 1, len(value)-1), '-', substring(value, len(value), 1)) from data;create table data(value varchar(100));
Here is the full example:
create table data(value varchar(100));
insert into data values('6789567');
insert into data values('98765434');
insert into data values('1234567');
insert into data values('876545');
insert into data values('342365');
select concat(substring(value, 1, len(value)-1), '-', substring(value, len(value), 1)) from data;
| (No column name) |
| :--------------- |
| 678956-7 |
| 9876543-4 |
| 123456-7 |
| 87654-5 |
| 34236-5 |
In case OP meant there can be multiple numbers in the column value here is the solution:
create table data1(value varchar(100));
insert into data1 values('6789567,5467474,846364');
insert into data1 values('98765434,6474644,76866,68696');
insert into data1 values('1234567,35637373');
select t.value, string_agg(concat(substring(token.value, 1, len(token.value)-1), '-',
substring(token.value, len(token.value), 1)), ',') as result
from data1 t cross apply string_split(value, ',') as token group by t.value;
value | result
:--------------------------- | :-------------------------------
1234567,35637373 | 123456-7,3563737-3
6789567,5467474,846364 | 678956-7,546747-4,84636-4
98765434,6474644,76866,68696 | 9876543-4,647464-4,7686-6,6869-6

Using SQL SERVER 2017, you can leverage STRING_SPLIT, STUFF, & STRING_AGG to handle this fairly easily.
DECLARE #T TABLE (val VARCHAR(100)) ;
INSERT INTO #T (val) VALUES ('8601881,9700800,2170') ;
SELECT t.val,
STRING_AGG(STUFF(ss.value, LEN(ss.value), 0, '-'), ',') AS Parsed
FROM #T AS t
CROSS APPLY STRING_SPLIT(t.val, ',') AS ss
GROUP BY t.val ;
Returns
8601881,9700800,2170 => 860188-1,970080-0,217-0
STRING_SPLIT breaks them into individual values, STUFF inserts the hyphen into each individual value, STRING_AGG combines them back into a single row per original value.

You can use LEN and LEFT/RIGHT method to get your desired output. Logic are given below:
Note: this will work for any length's value.
DECLARE #T VARCHAR(MAX) = '8601881'
SELECT LEFT(#T,LEN(#T)-1)+'-'+RIGHT(#T,1)

If you have "dash/hyphen" in your data, and you have to store it in varchar or nvarchar just append N before the data.
For example:
insert into users(id,studentId) VALUES (6,N'12345-1001-67890');

Related

select string between 3rd and 4th pipe delimiter

I have a column that contains values such as
Column
Asset|Class1|Category1|Group1|Account1
Expense|Class23|Category23|Group23|Account23
I want to select the string between 3rd and 4th occurrence of my pipe delimiter, how can I achieve this?
I've tried the PARSENAME and charindex+stuff function, but they have limitations, like max 128 characters. Also our SQL server has limited regex support. Any ideas?
SELECT REVERSE(PARSENAME(REVERSE(replace(LTRIM(Column), '|', '.')), 3))
My select need to return:
Group1
Group23
Perhaps this will help
Example
Declare #YourTable table (ID int,[Column] varchar(max))
Insert Into #YourTable values
(1,'Asset|Class1|Category1|Group1|Account1')
,(2,'Expense|Class23|Category23|Group23|Account23')
Select ID
,SomeValue = convert(xml,'<x>' + replace([Column],'|','</x><x>')+'</x>').value('/x[3]','varchar(100)')
From #YourTable
Returns
ID SomeValue
1 Category1
2 Category23
You can also use STRING_SPLIT() if you have 2016+
CREATE TABLE T(
ID INT IDENTITY(1,1),
Str VARCHAR(45)
);
INSERT INTO T(Str) VALUES
('Asset|Class1|Category1|Group1|Account1'),
('Expense|Class23|Category23|Group23|Account23');
SELECT V Str
FROM (
SELECT Value V,
ROW_NUMBER() OVER(PARTITION BY ID ORDER BY ID) RN
FROM T CROSS APPLY STRING_SPLIT(Str, '|')
) TT
WHERE RN = 3;
Returns:
Str
---------
Category1
Category23

Create one json per one table row

I would like to create jsons from the data in the table.
Table looks like that:
|code |
+------+
|D5ABX0|
|MKT536|
|WAEX44|
I am using FOR JSON PATH which is nice:
SELECT [code]
FROM feature
FOR JSON PATH
but the return value of this query are three concatenated jsons in one row:
|JSON_F52E2B61-18A1-11d1-B105-00805F49916B |
+----------------------------------------------------------+
1 |[{"code":"D5ABX0"},{"code":"MKT536"},{"code":"WAEX44"}]|
I need to have each row to be a separate json, like that:
|JSON_return |
+---------------------+
1 |{"code":"D5ABX0"} |
2 |{"code":"MKT536"} |
3 |{"code":"WAEX44"} |
I was trying to use splitting function (CROSS APPLY) which needs to have a separator as a parameter but this is not a robust solution as the json could be more expanded or branched and this could separate not the whole json but the json inside the json:
;WITH split AS (
SELECT [json] = (SELECT code FROM feature FOR JSON PATH)
)
SELECT
T.StringElement
FROM split S
CROSS APPLY dbo.fnSplitDelimitedList([json], '},{') T
The output is:
|StringElement |
+---------------------+
1 |[{"code":"D5ABX0" |
2 |"code":"MKT536" |
3 |"code":"WAEX44"}] |
Is there a way to force sqlserver to create one json per row?
You'll need to use as subquery to achieve this; FOR JSON will create a JSON string for the entire returned dataset. This should get you what you're after:
CREATE TABLE #Sample (code varchar(6));
INSERT INTO #Sample
VALUES ('D5ABX0'),
('MKT536'),
('WAEX44');
SELECT (SELECT Code
FROM #Sample sq
WHERE sq.code = S.code
FOR JSON PATH)
FROM #Sample S;
DROP TABLE #Sample;
CREATE TABLE #Temp
(
ID INT IDENTITY(1, 1) ,
StringValue NVARCHAR(100)
);
INSERT INTO #Temp
( StringValue )
VALUES ( N'D5ABX0' -- StringValue - nvarchar(100)
),
( 'MKT536' ),
( 'WAEX44' );
SELECT ID,'[{"code:":'''''+StringValue+'''''}]' AS JSON_return FROM #Temp
DROP TABLE #Temp

SQL Server using the REPLACE function to replace values in a column spanning multiple rows

I have a table like this:
Rule | Mask |Replacement
---------------------------------
# # 12 | # |[^0-9]
# # 12 | # |[0-9]
That I constructed joining these two tables
Table 1
Mask_ID | Mask |Replacement
---------------------------------
1 | # |[^0-9]
2 | # |[0-9]
Table 2
Rule_ID | Rule
--------------
1 | # # 12
The result I want is this:
Rule | Expression
--------------------
# # 12 | [^0-9] [0-9] 12
I've been trying to use the REPLACE button to do this, but I've only been able to generate this result
Rule | Expression
--------------------
# # 12 | [^0-9] # 12
# # 12 | # [0-9] 12
I'm not sure how to get the REPLACE function to apply multiple rows to a single row. If anyone has any suggestions, I would appreciate it.
This is what I have so far, but its causing me to get the result I mentioned above:
SELECT
A.PointMask_CODE
,B.PointMasking_Rule_CODE
,B.Mask
,B.Escape_Character
,B.EscapedMaskRule
,REPLACE(A.PointMask_CODE, B.Mask, B.EscapedMaskRule)
FROM
tblStatusPointMasks_CORE A
LEFT JOIN
vwAORs_Status_PointMasks_EscapedRules B
ON
PointMask_CODE LIKE '%' + B.EscapedMask + '%' ESCAPE ISNULL(B.Escape_Character, '\')
For your given sample data, you could use a recursive common table expression (cte).
create table masks (mask_id int, mask varchar(32), replacement varchar(32));
insert into masks values
(1, '#', '[^0-9]')
,(2, '#', '[0-9]');
create table rules (rule_id int, rule_txt varchar(32));
insert into rules values
(1, '# # 12');
with cte as (
select
r.rule_id
, r.rule_txt
, masks = 0
from rules r
union all
select
r.rule_id
, rule_txt = convert(varchar(32),replace(r.rule_txt,m.mask,m.replacement))
, masks = r.masks+1
from masks m
inner join cte r
on r.rule_txt like '%'+m.mask+'%'
)
select top 1 *
from cte
order by masks desc
rextester demo: http://rextester.com/KAV58392
returns:
+---------+-----------------+-------+
| rule_id | rule_txt | masks |
+---------+-----------------+-------+
| 1 | [^0-9] [0-9] 12 | 2 |
+---------+-----------------+-------+
One more option, using string operations...
create table masks (mask_id int, mask varchar(32), replacement varchar(32));
insert into masks values
(1, '#', '[^0-9]')
,(2, '#', '[0-9]');
create table rules (rule_id int, rule_txt varchar(32));
insert into rules values
(1, '# # # # 12');
declare #Table Table (charval varchar(10))
declare #char varchar(10), #rule_txt varchar(50)
select #rule_txt=rule_txt FROM rules
while charindex(' ',#rule_txt)>0
begin
select #char=substring(#rule_txt,1,charindex(' ',#rule_txt)-1)
FROM rules
insert into #Table values (#char)
SET #rule_txt=RIGHT(#rule_txt,(len(#rule_txt)-charindex(' ',#rule_txt)))
END
insert into #Table values (#rule_txt)
select stuff((SELECT ' '+isnull(replacement,charval)
from #Table T left join masks M on M.mask=T.charval
for xml path('')),1,1,'')
drop table rules
drop table masks

Join contents of field and insert new record

I have two tables in the following structure
Table - MemoType
ID | MemoTypeID | MemoTypeName
1 1234 A
2 5678 B
Table - Memos
ID | MemoTypeID | Memo | ExtRef
1 1234 TextOne XYZ
2 5678 TextTwo XYZ
3 1234 TextThree TUV
We would like to update these tables to reflect the following data
Table - MemoType
ID | MemoTypeID | MemoTypeName
3 9999 NewCombinedMemo
Table - Memos
ID | MemoTypeID | Memo | ExtRef
4 9999 <A> TextOne <B> TextTwo XYZ
5 9999 <A> TextThree TUV
The memos table has about 2 million rows with about 200,000 unique values for ExtRef.
My thinking is along the following lines (using .NET): Populate a List of all unique ExtRef values from Memos table; For each unique ExtRef get a list of all Memo values; concatenate strings as required; insert new record for each ExtRef; delete rest of the records for each ExtRef. The problem is that this would result in a large number of sql operations.
Please suggest if there are other efficient strategies to achieve this directly in SQL.
This is indeed possible directly through SQL, the following creates table variables to demonstrate / test with sample data and doesn't delete the original data.
The orginal data could easily be deleted using a clause checking on the memo type id, but I'd want to hold off on that until I'd performed a manual check on such a large table!
-- setting the scene
DECLARE #MemoType TABLE
(
Id int,
MemoTypeId int,
MemoTypeName varchar(30)
)
DECLARE #Memo TABLE
(
Id int identity(1,1),
MemoTypeId int,
Memo varchar(500),
ExtRef varchar(1000)
)
INSERT INTO #MemoType VALUES (1,1234,'A');
INSERT INTO #MemoType VALUES (2,1234,'B');
INSERT INTO #MemoType VALUES (3,9999,'NewCombinedMemo');
INSERT INTO #Memo VALUES (1234, 'TextOne', 'XYZ');
INSERT INTO #Memo VALUES (5678, 'TextTwo', 'XYZ');
INSERT INTO #Memo VALUES (1234, 'TextThree', 'TUV');
WITH cte(id, memotype, memotext, ref) as (
SELECT Id, MemoTypeId, Memo, ExtRef FROM #Memo
)
INSERT INTO #memo
SELECT 9999, stuff(memos,1,1,''),ref
FROM cte [outer]
CROSS APPLY (
SELECT ',' + memotext
FROM cte [inner]
WHERE [outer].ref = [inner].ref
FOR XML PATH('')
) n(memos)
GROUP BY ref, memos
select * from #memo
The CTE logic/description was borrowed from string concatenate in group by function with other aggregate functions - adding in logic to insert and strip out the leading comma.
I placed your original query in a CTE.
Then I cross applied with a subquery that gets
a comma-delimited set of memos for each reference in the outer query.
Since I also selected the memos column, I had to also group by the
memos column.
An initial comma needed to be stripped out with the stuff function
Finally, the result is inserted.

SQL Server 2000 equivalent of GROUP_CONCAT function

I tried to use the GROUP_CONCAT function in SQL Server 2000 but it returns an error:
'group_concat' is not a recognized function name"
So I guess there is an other function for group_concat in SQL Server 2000? Can you tell me what it is?
Unfortunately since you are using SQL Server 2000 you cannot use FOR XML PATH to concatenate the values together.
Let's say we have the following sample Data:
CREATE TABLE yourtable ([id] int, [name] varchar(4));
INSERT INTO yourtable ([id], [name])
VALUES (1, 'John'), (1, 'Jim'),
(2, 'Bob'), (3, 'Jane'), (3, 'Bill'), (4, 'Test'), (4, '');
One way you could generate the list together would be to create a function. A sample function would be:
CREATE FUNCTION dbo.List
(
#id int
)
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE #r VARCHAR(8000)
SELECT #r = ISNULL(#r+', ', '') + name
FROM dbo.yourtable
WHERE id = #id
and Name > '' -- add filter if you think you will have empty strings
RETURN #r
END
Then when you query the data, you will pass a value into the function to concatenate the data into a single row:
select distinct id, dbo.list(id) Names
from yourtable;
See SQL Fiddle with Demo. This gives you a result:
| ID | NAMES |
-------------------
| 1 | John, Jim |
| 2 | Bob |
| 3 | Jane, Bill |
| 4 | Test |