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.
Related
Can anyone please help me to write a SQL query. I just need the first number after the first backslash "" Please see the desired output. Thank you
ColumnName
Desire Output
\2610
2610
\2610\1178_2018 Bake Inn
2610
\2610\1178_2018 Change Outlook\2018-03-12 AOR & Page 33 DL
2610
\995645\1178_2018 Creek Choc
995645
\995645\1178_2018 Record Request\Club & Volume 12
995645
\995645
995645
\4323234\54724_2020 Add Image\2021-2019 Details Open\Checkbox-06- Value
4323234
\4323234
4323234
Please try the following solution.
It is based on XQuery. Its data model is based on ordered sequences. Exactly what we need for the task. We can get to 1st token, 2nd token, last token, any token in a sequence right away.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, tokens VARCHAR(MAX));
INSERT INTO #tbl (tokens) VALUES
('\2610'),
('\2610\1178_2018 Bake Inn'),
('\2610\1178_2018 Change Outlook\2018-03-12 AOR & Page 33 DL'),
('\995645\1178_2018 Creek Choc'),
('\995645\1178_2018 Record Request\Club & Volume 12'),
('\995645'),
('\4323234\54724_2020 Add Image\2021-2019 Details Open\Checkbox-06- Value'),
('\4323234');
-- DDL and sample data population, end
SELECT ID, tokens
, c.value('(/root/r[2]/text())[1]', 'VARCHAR(20)') AS [LeadingToken]
FROM #tbl
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' +
REPLACE(tokens, '\', ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)) AS t(c);
Output
+----+-------------------------------------------------------------------------+---------------+
| ID | tokens | LeadingToken |
+----+-------------------------------------------------------------------------+---------------+
| 1 | \2610 | 2610 |
| 2 | \2610\1178_2018 Bake Inn | 2610 |
| 3 | \2610\1178_2018 Change Outlook\2018-03-12 AOR & Page 33 DL | 2610 |
| 4 | \995645\1178_2018 Creek Choc | 995645 |
| 5 | \995645\1178_2018 Record Request\Club & Volume 12 | 995645 |
| 6 | \995645 | 995645 |
| 7 | \4323234\54724_2020 Add Image\2021-2019 Details Open\Checkbox-06- Value | 4323234 |
| 8 | \4323234 | 4323234 |
+----+-------------------------------------------------------------------------+---------------+
I have 2 string columns (thousands of rows) with ordered numbers in each string (there can be zero to ten numbers in each string). Example:
+------------------+------------+
| ColString1 | ColString2 |
+------------------+------------+
| 1;3;5;12; | 4;6' |
+------------------+------------+
| 1;5;10 | 2;26; |
+------------------+------------+
| 4;7; | 3; |
+------------------+------------+
The end result is to combine these 2 columns, sort the numbers in
ascending order and then put each number into individual columns (smallest, 2nd smallest etc).
e.g. Colstring1 is 1;3;5;12; and ColString2 is 4;6; needs to return 1;3;4;5;6;12; which I then use xml to allocated into columns.
Everthing works fine using xml apart from the step to order the numbers (i.e I'm getting 1;3;5;12;4;6; when I combine the strings i.e. not in ascending order).
I've tried put them into a JSON array first to order, thinking I could do a top[1] etc but that did not work.
Any help on how to combine the 2 columns and order them before inserting into columns:
Steps so far:
Example data:
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, ColString1 VARCHAR(50), ColString2 VARCHAR(50));
INSERT INTO #tbl (ColString1, ColString2)
VALUES
('1;3;5;12;', '4;6;'),
('1;5;10;', '2;26;'),
('14;', '3;8;');
XML Approach (Combines strings and puts into columns but not in the correct order):
;WITH Split_Numbers (xmlname)
AS
(
SELECT
CONVERT(XML,'<Names><name>'
+ REPLACE ( LEFT(ColString1+ColString2,LEN(ColString1+ColString2) - 1),';', '</name><name>') + '</name></Names>') AS xmlname
FROM #tbl
)
SELECT
xmlname.value('/Names[1]/name[1]','int') AS Number1,
xmlname.value('/Names[1]/name[2]','int') AS Number2,
xmlname.value('/Names[1]/name[3]','int') AS Number3,
xmlname.value('/Names[1]/name[4]','int') AS Number4,
xmlname.value('/Names[1]/name[5]','int') AS Number5
--etc for additional columns
FROM Split_Numbers
Current Output: numbers not in correct order,
+---------+---------+---------+---------+---------+
| Number1 | Number2 | Number3 | Number4 | Number5 |
+---------+---------+---------+---------+---------+
| 1 | 3 | 5 | 12 | 4 |
| 1 | 5 | 10 | 2 | 26 |
| 14 | 3 | 8 | NULL | NULL |
+---------+---------+---------+---------+---------+
Desired Output: numbers in ascending order.
+---------+---------+---------+---------+---------+
| Number1 | Number2 | Number3 | Number4 | Number5 |
+---------+---------+---------+---------+---------+
| 1 | 3 | 4 | 5 | 6 |
| 1 | 2 | 5 | 10 | 26 |
| 3 | 8 | 14 | NULL | NULL |
+---------+---------+---------+---------+---------+
JSON Approach: combines the columns into a JSON array but I still can't order correctly when in JSON format.
REPLACE ( CONCAT('[', LEFT(ColString1+ColString2,LEN(ColString1+ColString2) - 1), ']') ,';',',')
Any help will be greatly appreciated whether there is a way to order the xml or JSON string prior to entry. Happy to consider an alternative way if there is an easier solution.
You can use string_agg() and string_split():
select t.*, newstring
from t cross apply
(select string_agg(value, ',') order by (value) as newstring
from (select s1.value
from unnest(colstring1, ',') s1
union all
select s2.value
from unnest(colstring2, ',') s2
) s
) s;
That said, you should probably put your effort into fixing the data model. Storing numbers in strings is bad. Storing multiple values in a string is bad, bad. If the numbers are foreign references to other tables, that is bad, bad, bad, bad, bad.
While waiting for a DDL and sample data population, etc., here is a conceptual example for you. It is using XQuery and its FLWOR expression.
CTE does most of the heavy lifting:
Concatenates both columns values into one string. CONCAT() function protects against NULL values.
Converts it into XML data type.
Sorts XML elements by converting their values to int data type in the FLWOR expression.
Filters out XML elements with no legit values.
The rest is trivial.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, col1 VARCHAR(100), col2 VARCHAR(100));
INSERT INTO #tbl (col1, col2)
VALUES
('1;3;5;12;', '4;6;'),
('1;5;10;', '2;26;');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = ';';
;WITH rs AS
(
SELECT *
, CAST('<root><r><![CDATA[' +
REPLACE(CONCAT(col1, col2), #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML).query('<root>
{
for $x in /root/r[text()]
order by xs:int($x)
return $x
}
</root>') AS sortedXML
FROM #tbl
)
SELECT ID
, c.value('(r[1]/text())[1]','INT') AS Number1
, c.value('(r[2]/text())[1]','INT') AS Number2
, c.value('(r[3]/text())[1]','INT') AS Number3
-- continue with the rest of the columns
FROM rs CROSS APPLY sortedXML.nodes('/root') AS t(c);
Output
+----+---------+---------+---------+
| ID | Number1 | Number2 | Number3 |
+----+---------+---------+---------+
| 1 | 1 | 3 | 4 |
| 2 | 1 | 2 | 5 |
+----+---------+---------+---------+
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
How can I make the following sql result:
Be rendered in a result set like this
| ID | Chain Size | Length | Hook Type | Shortening Grab |
|-----|-----------------|---------|------------|-----------------|
| 163 | 7mm (1.5 tonne) | 1 metre | C Hook | Yes |
| 226 | 7mm (1.5 tonne) | 1 metre | C Hook | No |
| 247 | 7mm (1.5 tonne) | 1 metre | Latch Hook | No |
I know that the values in columns 2,4,6 and 8 (which I want to be headers) will be the same across all rows (but different depending on the initial query).
I believe the approach for what I want is through the use of PIVOT but really struggling to get the desired result.
Thanks
Assuming your source data actually looks like this:
Static Pivot
Select *
From YourTable
Pivot (max(attributeValue) For [attributeName] in ([Chain Size],[Length],[Hook Type],[Shortening Grab]) ) p
Returns
Dynamic Approach
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName([attributeName]) From Yourtable Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select *
From YourTable
Pivot (max(attributeValue) For [attributeName] in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
Notice, without an item for column sequence, you'll see that they are alphabetical.
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