Extract characters after a symbol in sql server 2012 - sql

I have a table List shown below:
+------+-------------------------------------+
| Code | name |
+------+-------------------------------------+
| A001 | ABBOTT_1000000 |
| A002 | AGCO_1000001 |
| A003 | ALFA LAVAL_1000002 |
| A004 | ALSTOM POWER INDIA LIMITED_1000003 |
| A005 | AMERICAN BUREAU OF SHIPPING_1000004 |
+------+-------------------------------------+
I need to update the table extracting the characters present after _ in name and replace them in code column. like this.
+---------+-------------------------------------+
| Code | name |
+---------+-------------------------------------+
| 1000000 | ABBOTT_1000000 |
| 1000001 | AGCO_1000001 |
| 1000002 | ALFA LAVAL_1000002 |
| 1000003 | ALSTOM POWER INDIA LIMITED_1000003 |
| 1000004 | AMERICAN BUREAU OF SHIPPING_1000004 |
+---------+-------------------------------------+
This is has to be done in sql server 2012. please help me.

Try this
with cte as
(
select substring(name,charindex('_',name)+1,len(name)) as ext_str,*
from yourtable
)
update cte set code = ext_str

You can try to use SUBSTRING in following:
SAMPLE DATA
CREATE TABLE #MyTable
(
Code NVARCHAR(60),
Name NVARCHAR(60)
)
INSERT INTO #MyTable VALUES
('A001','ABBOTT_1000000' ),
('A002','AGCO_1000001' ),
('A003','ALFA LAVAL_1000002' ),
('A004','ALSTOM POWER INDIA LIMITED_1000003' ),
('A005','AMERICAN BUREAU OF SHIPPING_1000004' )
QUERY
UPDATE #MyTable
SET Code = SUBSTRING(Name, CHARINDEX('_', Name) + 1, LEN(Name))
TESTING
SELECT * FROM #MyTable
DROP TABLE #MyTable
OUTPUT
Code Name
1000000 ABBOTT_1000000
1000001 AGCO_1000001
1000002 ALFA LAVAL_1000002
1000003 ALSTOM POWER INDIA LIMITED_1000003
1000004 AMERICAN BUREAU OF SHIPPING_1000004
SQL FIDDLE
DEMO

UPDATE <table>
SET name = STUFF(name, 1, CHARINDEX('_', name), '')
WHERE name like '%[_]%'

You can do this:
First, you select the number that you want to place as code
select substr(name,-1,7) from table_name
After this, you can update the table.So,The complete statement will be :
update table_name t set t.code = (select substr(name,-1,7) from table_name where code = t.code);

You can use RIGHT together with CHARINDEX:
SQL Fiddle
UPDATE tbl
SET Code = RIGHT(Name, LEN(Name) - CHARINDEX('_', Name))
WHERE CHARINDEX('_', Name) > 0

Related

How can I remove the spaces from these numbers?

I am using SQL Server 2014 and I have a table (t1) in my database which contain a column called "MealPlan".
This column contains a list of strings (extract below):
MealPlan
Sansrepas315€/pers.=630€pour2pers.Devis/RésaSelectionner
Sansrepas394€/pers.=787€pour2pers.Devis/RésaSelectionner
Sansrepas547€/pers.=1 093€pour2pers.Devis/RésaSelectionner
Sansrepas547€/pers.=1 093€pour2pers.Devis/RésaSelectionner
Sansrepas700€/pers.=1 400€pour2pers.Devis/RésaSelectionner
Sansrepas328€/pers.=656€pour2pers.Devis/RésaSelectionner
I need to extract the numbers between the characters = and €
I have the following codes in place which does exactly what I need:
SUBSTRING(MealPlan,LEN(LEFT(MealPlan,CHARINDEX('=', MealPlan)+1)),LEN(MealPlan) - LEN(LEFT(MealPlan,CHARINDEX('=', MealPlan))) - LEN(RIGHT(MealPlan,CHARINDEX('€', (REVERSE(MealPlan)))))) AS [Price]
After running the above my column "Price" appear as follows:
Price
630
787
1 093
1 093
1 400
656
However, I want to get rid of that space in the numbers where a thousand digit is present.
My expected output:
Price
630
787
1093
1093
1400
656
I have tried the following but it is not working:
REPLACE(SUBSTRING(MealPlan,LEN(LEFT(MealPlan,CHARINDEX('=', MealPlan)+1)),LEN(MealPlan) - LEN(LEFT(MealPlan,CHARINDEX('=', MealPlan))) - LEN(RIGHT(MealPlan,CHARINDEX('€', (REVERSE(MealPlan)))))), ' ','') AS [Price2]
Any help would be much appreciated.
I just ran your query in my sample database and it is working fine..
select
REPLACE(SUBSTRING(Description,
LEN(LEFT(Description,CHARINDEX('=', Description)+1)),
LEN(Description) - LEN(LEFT(Description,CHARINDEX('=', Description))) - LEN(RIGHT(Description,CHARINDEX('€', (REVERSE(Description)))))
), ' ','') AS [Description]
from Worker
Table #a1
| MealPlan |
| -------- |
| Sansrepas315€/pers.=630€pour2pers.Devis/RésaSelectionner |
| Sansrepas394€/pers.=787€pour2pers.Devis/RésaSelectionner |
| Sansrepas547€/pers.=1 093€pour2pers.Devis/RésaSelectionner |
Query
SELECT
REPLACE(
SUBSTRING(MealPlan,CHARINDEX('=', MealPlan)+1, CHARINDEX('=',REVERSE(MealPlan)) - CHARINDEX('€',REVERSE(MealPlan)) -1 )
,' ', ''
)
as value
FROM #a1
results
value
630
787
1093
The solution about "cut and paste" provided by Jiří Baum above did the trick for me.
It is very easy to tokenize a string of characters by using XML and XQuery.
No need to parse string and call multiple functions: SUBSTRING(), CHARINDEX(), PATINDEX(), LEN(), REVERSE(), etc.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY(1,1) PRIMARY KEY, MealPlan NVARCHAR(1000));
INSERT INTO #tbl (MealPlan) VALUES
(N'Sansrepas315€/pers.=630€pour2pers.Devis/RésaSelectionner'),
(N'Sansrepas394€/pers.=787€pour2pers.Devis/RésaSelectionner'),
(N'Sansrepas547€/pers.=1 093€pour2pers.Devis/RésaSelectionner'),
(N'Sansrepas547€/pers.=1 093€pour2pers.Devis/RésaSelectionner'),
(N'Sansrepas700€/pers.=1 400€pour2pers.Devis/RésaSelectionner'),
(N'Sansrepas328€/pers.=656€pour2pers.Devis/RésaSelectionner');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = '='
, #euro CHAR(1) = '€';
SELECT t.*
, REPLACE(c.value('(/root/r[3]/text())[1]', 'VARCHAR(20)'),SPACE(1),'') AS Price
FROM #tbl AS t
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' +
REPLACE(REPLACE(MealPlan,#euro,#separator), #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)) AS t1(c);
Output
+----+------------------------------------------------------------+--------+
| ID | MealPlan | Result |
+----+------------------------------------------------------------+--------+
| 1 | Sansrepas315€/pers.=630€pour2pers.Devis/RésaSelectionner | 630 |
| 2 | Sansrepas394€/pers.=787€pour2pers.Devis/RésaSelectionner | 787 |
| 3 | Sansrepas547€/pers.=1 093€pour2pers.Devis/RésaSelectionner | 1093 |
| 4 | Sansrepas547€/pers.=1 093€pour2pers.Devis/RésaSelectionner | 1093 |
| 5 | Sansrepas700€/pers.=1 400€pour2pers.Devis/RésaSelectionner | 1400 |
| 6 | Sansrepas328€/pers.=656€pour2pers.Devis/RésaSelectionner | 656 |
+----+------------------------------------------------------------+--------+
Try this:
Declare #MealPlan as varchar(250) = 'Sansrepas547€/pers.=1 093€pour2pers.Devis/RésaSelectionner'
Select Replace(
SUBSTRING(#MealPlan,
CharIndex('=', #MealPlan)+1,
CharIndex(')',Replace(#MealPlan, '€p', ')'))- CharIndex('=', #MealPlan)-1 ),
' ', '')
Output:
Let me know if this doesn't work or you have a problem understanding the query.

SQL Server stored procedure inserting duplicate rows

I have a table with column GetDup and I'd like to the duplicate records based on the value of this column. For example, if value on is 1 in GetDup, then duplicate the record once. If value in the column is 2, then duplicate the record twice and so on and the statement has to be in looping statement.
What will be a good way to write a stored procedures for this? Please help.
Input:
+--------+--------------+---------------+
| Getdup | CustomerName | CustomerAdd |
+--------+--------------+---------------+
| 1 | John | 123 SomeWhere |
| 2 | Bob | 987 SomeWhere |
+--------+--------------+---------------+
What I want:
+--------+--------------+---------------+
| Getdup | CustomerName | CustomerAdd |
+--------+--------------+---------------+
| 1 | John | 123 SomeWhere |
| 1 | John | 123 SomeWhere |
| 2 | Bob | 987 SomeWhere |
| 2 | Bob | 987 SomeWhere |
| 2 | Bob | 987 SomeWhere |
+--------+--------------+---------------+
picture of data
Answer #2 After Clarification
Number Table to the Rescue!
The number table in my example (or tally table, if you want to call it that), is both temporary and very small. To make it bigger, just add more values to z and add more CROSS JOINs. In my opinion, a number table and a calendar table are both things that should be in every database you have. They are extremely useful.
SQL Fiddle
MS SQL Server 2017 Schema Setup:
CREATE TABLE mytable ( Getdup int, CustomerName varchar(10), CustomerAdd varchar(20) ) ;
INSERT INTO mytable (Getdup, CustomerName, CustomerAdd)
VALUES (1,'John','123 SomeWhere'), (2,'Bob','987 SomeWhere')
;
Query 1:
;WITH z AS (
SELECT *
FROM ( VALUES(0),(0),(0),(0) ) v(x)
)
, numTable AS (
SELECT num
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY z1.x)-1 num
FROM z z1
CROSS JOIN z z2
) s1
)
SELECT t1.Getdup, t1.CustomerName, t1.CustomerAdd
FROM mytable t1
INNER JOIN numTable ON t1.getdup >= numTable.num
ORDER BY CustomerName, CustomerAdd
Results:
| Getdup | CustomerName | CustomerAdd |
|--------|--------------|---------------|
| 2 | Bob | 987 SomeWhere |
| 2 | Bob | 987 SomeWhere |
| 2 | Bob | 987 SomeWhere |
| 1 | John | 123 SomeWhere |
| 1 | John | 123 SomeWhere |
--------------------------------------------------------------------------
ORIGINAL ANSWER
EDIT: After further clarification of the problem, this won't duplicate rows, this will only duplicate the data in a column.
Something like one of these might work.
T-SQL
SELECT replicate(mycolumn,getdup) AS x
FROM mytable
MySQL
SELECT repeat(mycolumn,getdup) AS x
FROM mytable
Oracle SQL
SELECT rpad(mycolumn,getdup*length(mycolumn),mycolumn) AS x
FROM mytable
PostgreSQL
SELECT repeat(mycolumn,getdup+1) AS x
FROM mytable
If you can provide more details for exactly what you want and what you're working with, we might be able to help you better.
NOTE 2: Depending on what you need, you may need to do some math magic. You say above if GetDup is 1 then you want one duplicate. If that means that your output should be GetDup``GetDup, then you'll want to add one in the repeat(),replicate() or rpad() functions. ie replicate(mycolumn,getdup+1). Oracle SQL will be a little different, since it uses rpad().
In standard SQL you can use a recursive CTE:
with recursive cte as (
select t.dup, . . .
from t
union all
select cte.dup - 1, . . .
from cte
where cte.dup > 1
)
select *
from cte;
Of course, not all databases support recursive CTEs (and the recursive keyword is not used in some of them).
So, you want recursive solution :
with t as (
select Getdup, CustomerName, CustomerAdd, 0 as id
from table
union all
select Getdup, CustomerName, CustomerAdd, id + 1
from t
where id < getdup
)
insert into table (col1, col2, col3)
select Getdup, CustomerName, CustomerAdd
from t
order by getdup
option (maxrecursion 0);

Substring output for different cases

I need output with following cases:
+-----------------+--------+
| STRING in table | OUTPUT |
+-----------------+--------+
| NONGL_NONGL | NONGL |
| GL252_GL252 | GL |
| GL400_GL400 | GL |
| NOS_NOS | NOS |
+-----------------+--------+
I tried to use SUBSTRING() but it is not giving me proper output.
So for the NONGL and NOS result you need:
SELECT LEFT([col_name], CHARINDEX('_', [col_name]) - 1)
And for the GL output, since the string is containing numbers you need PATINDEX():
SELECT LEFT([col_name], PATINDEX('%[0-9]%', [col_name]) - 1)
In the end just use a CASE WHEN
DECLARE #tbl Table (string VARCHAR(MAX) );
INSERT INTO #tbl VALUES
('NONGL_NONGL'),
('GL252_GL252'),
('GL400_GL400'),
('NOS_NOS');
SELECT
string
,CASE WHEN
string LIKE '%[0-9]%' then LEFT(string, PATINDEX('%[0-9]%', string) - 1)
ELSE LEFT(string, CHARINDEX('_', string) - 1)
END AS [Output]
from #tbl
This should do what you want:
select (case when col like 'NONGL%' then 'NONGL'
when col like 'GL%' then 'GL'
when col like 'NOS%' then 'NOS'
end) as new_col
Here is another way:
DECLARE #StrTable Table (Str VARCHAR(MAX) );
INSERT INTO #StrTable VALUES
('NONGL_NONGL'),
('GL252_GL252'),
('GL400_GL400'),
('NOS_NOS');
SELECT Str,
IIF(Str LIKE 'NONGL%','NONGL',IIF(Str LIKE 'GL%', 'GL', IIF(Str LIKE 'NOS%', 'NOS', Str))) AS Outputs
FROM #StrTable;
Result:
+-------------+---------+
| Str | Outputs |
+-------------+---------+
| NONGL_NONGL | NONGL |
| GL252_GL252 | GL |
| GL400_GL400 | GL |
| NOS_NOS | NOS |
+-------------+---------+

SQL Sort Numeric Strings After Split

I currently have char values in a table column which are in the format "IS-" and then 1 to 5 numbers, a possible period with either 2 numbers or a letter following the period.
Examples are, IS-1, IS-12, IS-123, IS-123.11, IS-123.a.
I need to split the string so that I grab only the number part, sort the strings ASC, and the bring the strings back together the way they were.
Explanation. I have the following set of values, IS-1170, IS-1171, IS-1172, IS-1173, IS-1174, IS-870.a, IS-871.a, IS-872.a. As you can see, because IS-1 comes before IS-8 they are sorting out of numerical order.
Any idea where to begin? I was thinking of using CASE, but I'm not really sure how to proceed.
Thanks.
Do string functions in your ORDER BY to remove only the number. Something like this should work:
SELECT col
FROM table
ORDER BY CAST(CASE WHEN ISNUMERIC(SUBSTRING(col,4,20)) = 1
THEN SUBSTRING(col,4,20)
ELSE LEFT(SUBSTRING(col,4,20),CHARINDEX('.',SUBSTRING(col,4,20),0)-1)
END AS NUMERIC)
This will first remove the IS- and check if the rest of the string is a number. If it is, it will leave the decimal digits, otherwise it will remove the . and the following alpha characters.
This is assuming your intended ordering in the case of numeric decimal places would be:
IS-123.A
IS-123.1
IS-123.2
If you don't care about what's after the decimal/period, then simply:
ORDER BY CAST(LEFT(SUBSTRING(col,4,20),CHARINDEX('.',SUBSTRING(col,4,20),0)-1) AS NUMERIC)
If I understand you correctly, this might help you:
DECLARE #mockup TABLE(ID INT IDENTITY,YourExample VARCHAR(100));
INSERT INTO #mockup VALUES
('IS-1, IS-12, IS-123, IS-123.11, IS-123.a.')
,('IS-1170, IS-1171, IS-1172, IS-1173, IS-1174, IS-870.a, IS-871.a, IS-872.a');
WITH Splitted AS
(
SELECT *
,CAST('<x>' + REPLACE(m.YourExample,',','</x><x>') + '</x>' AS XML) AS SplitAtComma
FROM #mockup AS m
)
,NumberExtracted AS
(
SELECT s.ID
,part.value('text()[1]','nvarchar(max)') AS OnePart
,CAST('<y>' + REPLACE(REPLACE(part.value('text()[1]','nvarchar(max)'),'.','-'),'-','</y><y>') + '</y>' AS XML).value('/y[2]/text()[1]','int') AS TheNumber
FROM Splitted AS s
CROSS APPLY s.SplitAtComma.nodes('/x') AS A(part)
)
SELECT *
FROM NumberExtracted
ORDER BY ID,TheNumber;
The first CTE uses a string-split via XML to get all values within the original string (btw: never store comma separated values!).
The second CTE will use the same approach to extract the number, typesafe as INT.
You can use this in an ORDER BY finally.
The result:
+----+-----------+-----------+
| ID | OnePart | TheNumber |
+----+-----------+-----------+
| 1 | IS-1 | 1 |
+----+-----------+-----------+
| 1 | IS-12 | 12 |
+----+-----------+-----------+
| 1 | IS-123 | 123 |
+----+-----------+-----------+
| 1 | IS-123.11 | 123 |
+----+-----------+-----------+
| 1 | IS-123.a. | 123 |
+----+-----------+-----------+
| 2 | IS-870.a | 870 |
+----+-----------+-----------+
| 2 | IS-871.a | 871 |
+----+-----------+-----------+
| 2 | IS-872.a | 872 |
+----+-----------+-----------+
| 2 | IS-1170 | 1170 |
+----+-----------+-----------+
| 2 | IS-1171 | 1171 |
+----+-----------+-----------+
| 2 | IS-1172 | 1172 |
+----+-----------+-----------+
| 2 | IS-1173 | 1173 |
+----+-----------+-----------+
| 2 | IS-1174 | 1174 |
+----+-----------+-----------+
IF OBJECT_ID(N'tempdb..##table1', N'U') IS NOT NULL
DROP TABLE ##table1;
create table ##table1(col1 varchar(20))
declare #query as nvarchar(max)
declare #var1 as varchar(max)='IS-1, IS-12, IS-123, IS-123.11, IS-123.a.,IS-1170, IS-1171, IS-1172, IS-1173, IS-1174, IS-870.a, IS-871.a, IS-872.a.'
set #var1=replace(#var1,',','''),(''')
set #var1='('''+#var1+''')'
set #var1=replace(#var1,' ','')
set #query='insert into ##table1 values'+#var1
EXEC sp_executesql #query
IF OBJECT_ID(N'tempdb..##table2', N'U') IS NOT NULL
DROP TABLE ##table2;
select * into ##table2 from ##table1 order by cast(replace(replace(replace(col1,'IS-',''),'.a.',''),'.a','') as float)
declare #results varchar(max)
select #results = coalesce(#results + ', ', '') + convert(varchar(12),col1) from ##table2
select #results
DROP TABLE ##table1
DROP TABLE ##table2

Concat multiple rows PSQL

id | name | Subject | Lectured_Times | Faculty
3258132 | Chris Smith | SATS1364 | 10 | Science
3258132 | Chris Smith | ECTS4605 | 9 | Engineering
How would I go about creating the following
3258132 Chris Smith SATS1364, 10, Science + ECTS4605, 9,Engineering
where the + is just a new line. Notice how after the '+'(new line) it doesnt concat the id,name
try
SELECT distinct concat(id,"name",string_agg(concat(subject, Lectured_Times , Faculty), chr(10)))
from tn
where id = 3258132
group by id;
As mentioned above string_agg is perfect solution for this.
select
id, name, string_agg(concat(subject, Lectured_Times, Faculty), '\n')
from table
group by id, name