Convert XML to table SQL Server 2005 - sql

I wonder how I can read a XML data and transform it to a table in T-SQL?
For example:
<t1>
<t2>
<val>Opel</val>
<t3>Merriva</t3>
<t3>Zafira</t3>
</t2>
<t2>
<val>Fiat</val>
<t3>Albea</t3>
</t2>
</t1>
To:
Table1:
id value
----------------
1 Opel
2 Fiat
Table2:
id id_Table1 value
-----------------------------------
1 1 Merriva
2 1 Zafira
3 2 Albea

I don't have an active SQL-Server 2005 to test this (boy, it's 2018...!!!), but I think, that this query would work in such ancient versions too:
DECLARE #xml XML=
N'<t1>
<t2>
<val>Opel</val>
<t3>Merriva</t3>
<t3>Zafira</t3>
</t2>
<t2>
<val>Fiat</val>
<t3>Albea</t3>
</t2>
</t1>';
--The CTE will return the CarName with a running index together with the related data as XML-node
WITH Cars AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS t2_index
,t2.value(N'(val/text())[1]',N'nvarchar(100)') AS t2_val
,t2.query(N't3') AS t3_nodes
FROM #xml.nodes(N'/t1/t2') A(t2)
)
--This part will append all related data with a running number for the related type data
SELECT Cars.*
,ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) AS CarValueIndex
,t3.value(N'text()[1]',N'nvarchar(100)') AS CarValue
INTO #tmpCars --write the result into a temp table
FROM Cars
OUTER APPLY t3_nodes.nodes(N't3') A(t3);
--All data de-normalized
SELECT * FROM #tmpCars;
--This query will bring back the parent rows
SELECT t2_index AS CarID
,t2_val AS CarName
FROM #tmpCars
GROUP BY t2_index,t2_val;
--And this query will return the related child data
SELECT CarValueIndex AS CarTypeID
,t2_index AS fk_CarID
,CarValue AS CarType
FROM #tmpCars;
GO
DROP TABLE #tmpCars;
If there is any chance to move to a more modern SQL-Server you really should do it...
UPDATE
According to this link, the .value() and .nodes() function were introduced with v2008. But I have a dark memory, that it was working in 2005 already, might be together with some service packs... Try it out.

Related

data type of each characters in a varchar T-sql

I'm curious on the data I get from someone. Most of the time I need to get 3 integers then a space then eight integers.
And The integration created a column varchar(20) ... Don't doubt it works, but that gives me some matching errors.
Because of this, I'd like to know what is the data type of the characters on each row.
For exemple : 0 is for integer, s for space, a for char, * for specific
AWB | data type
---------------------------------
012 12345678 | 000s00000000
9/5 ab0534 | 0*0saa0000
I'd like to know if there is a function or a formula to get this kind of results.
Right after I'll be able to group by this column and finally be able to check how good is the data quality.
I don't know if there is a specific word for what I tried to explain, so excuse me if this is a duplicate of a post, I didn't find it.
Thank you for your feedback.
There's nothing built-in, but you might use an approach like this:
DECLARE #tbl TABLE(ID INT IDENTITY,AWB VARCHAR(100));
INSERT INTO #tbl VALUES
('012 12345678')
,('9/5 ab0534');
WITH cte AS
(
SELECT t.ID
,t.AWB
,A.Nmbr
,C.YourMask
FROM #tbl t
CROSS APPLY (SELECT TOP (DATALENGTH(t.AWB)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) A(Nmbr)
CROSS APPLY (SELECT SUBSTRING(t.AWB,A.Nmbr,1)) B(SingleCharacter)
CROSS APPLY (SELECT CASE WHEN B.SingleCharacter LIKE '[0-9]' THEN '0'
WHEN B.SingleCharacter LIKE '[a-z]' THEN 'a'
WHEN B.SingleCharacter = ' ' THEN 's'
ELSE '*' END) C(YourMask)
)
SELECT ID
,AWB
,(
SELECT YourMask
FROM cte cte2
WHERE cte2.ID=cte.ID
ORDER BY cte2.Nmbr
FOR XML PATH(''),TYPE
).value('.','nvarchar(max)') YourMaskConcatenated
FROM cte
GROUP BY ID,AWB;
The idea in short:
The cte will create a derived set of your table.
The first CROSS APPLY will create a list of numbers as long as the current AWB value.
The second CROSS APPLY will read each character separately.
The third CROSS APPLY will finally use some rather simple logic to translate your values to the mask you expect.
The final SELECT will then use GROUP BY and a correlated sub-query with FOR XML to get the mask characters re-concatenated (With version v2017+ this would be easier calling STRING_AGG()).

Shred XML For Each Row in SQL Table

I have a table that contains two columns, and ID, and XML data. I'd like to shred the XML for each ID. I'm pulling out a single value within the XML and all the XML is structured the same I'm just not sure how to loop through the table and apply XML query to each row.
The query I need to apply is as follows:
Select top 1
Element1 = XV.value('(.)[1]','nvarchar(32)')
from #xml.nodes('Parameters/Parameter/Value') as x(XV)
So the end results would have two columns, ID and shredded value from XML.
Without any knowledge about your actual XML and how you want to shred it to get some values it is impossible to answer in completness, but this shoudl point you in the right direction:
Returns the ID and the XML as is
SELECT ID
,TheXmlColumn
FROM YourTable
This returns the ID and a value out of your XML
SELECT ID
,TheXmlColumn.value('Some XPaht','SomeType') AS SomeValueFromXML
FROM YourTable
And if there are more embedded rows it would be something like this
SELECT ID
,nd.value('Some XPaht','SomeType') AS SomeValueFromXMLRow
FROM YourTable
OUTER APPLY TheXmlColumn.nodes('SomeXPath') AS A(nd)
My magic glass bulb tells me, that you might need something like this:
SELECT ID
,TheXmlColumn.value('(Parameters/Parameter/Value)[1]','nvarchar(max)') AS SomeValueFromXML
FROM YourTable

select max of mixed string/int column

I have a table who returns me a value as bellow
id_unique name serie timeB timeD
155488EA-FF70-49D7-99AB-AFD4125F3435 dell 14296188 05:51 06:19
1B640883-0DB6-4255-B1ED-770B6578064C dell 14295943 04:37 04:39
I want the max of the value i have tried a sql as bellow :
select max(cast(id_unique as varchar(36))),
max(name),max(serie),max(timeB),max(timeD) from mytable group by name
i got this result
1B640883-0DB6-4255-B1ED-770B6578064C dell 14296188 05:51 06:19
But the result that i need is this one :
155488EA-FF70-49D7-99AB-AFD4125F3435 dell 14296188 05:51 06:19
This any metho to fix that ?
My guess is that you want something like this using row_number:
select *
from (
select *, row_number() over (order by somevalue desc) rn
from yourtable
) t
where rn = 1
Where somevalue is the column you need the max of. I presume you are currently trying to use max for every field -- that would explain your output...
If these are really stored as strings, I'm guessing that you want to cast the first column as a uniqueidentifier before finding the max, otherwise they will be evaluated alphabetically.
SQL Server 2012 and above
You can directly get the min/max of uniqueidentifiers:
select max(cast(id_unique as uniqueidentifier))
from MyTable
Previous Versions
You should cast as binary(16) before finding the min/max. I'm then casting back to uniqueidentifier for readability of the results:
select cast(max(cast(cast(id_unique as uniqueidentifier) as binary(16))) as uniqueidentifier)
from MyTable

SQL Server: Using CHARINDEX to parse multiplying problems

I have a data column that stores products distribution units as 1*1 or 1*2*6. I want to formulate a computed column and have the result of the multiply problem.
below is illustrated example
sku du computed_du
12345678 1*2 2
12345679 1*3 3
12345680 1*6*2 12
Is there a most effective way to perform this calculation on sql server?
This is definitely something that should be calculated elsewhere, but I have seen the below technique used as a crude product aggregation.
I am not positive about using this for computed column, but here is how you might pull it off: Wrap this in a function, schema bind it to your table, and reference it the computed column definition (not sure if mssql will allow this due to deterministic requirement of udf).
Uses split function from here.
declare #tab table (
sku int,
du varchar(10),
computed_du int
)
insert into #tab
select 12345678, '1*2', null union all
select 12345679, '1*3', null union all
select 12345680, '1*6*2*0', null
--
select sku, du, min(s), case when min(cast(s as int)) = 0 then 0 else exp(sum(log(cast(nullif(s, 0) as int)))) end
from #tab
cross
apply dbo.Split('*', du)d
where cast(s as int)>0
group
by sku, du;

Sql query that numerates the returned result

How to write one SQL query that selects a column from a table but returns two columns where the additional one contains an index of the row (a new one, starting with 1 to n). It must be without using functions that do that (like row_number()).
Any ideas?
Edit: it must be a one-select query
You can do this on any database:
SELECT (SELECT COUNT (1) FROM field_company fc2
WHERE fc2.field_company_id <= fc.field_company_id) AS row_num,
fc.field_company_name
FROM field_company fc
SET NOCOUNT ON
DECLARE #item_table TABLE
(
row_num INT IDENTITY(1, 1) NOT NULL PRIMARY KEY, --THE IDENTITY STATEMENT IS IMPORTANT!
field_company_name VARCHAR(255)
)
INSERT INTO #item_table
SELECT field_company_name FROM field_company
SELECT * FROM #item_table
if you are using Oracle or a database that supports Sequence objects, make a new db sequence object for this purpose. Next create a view, and run this.
insert into the view as select column_name, sequence.next from table
In mysql you can :
SELECT Row,Column1
FROM (SELECT #row := #row + 1 AS Row, Column1 FROM table1 )
As derived1
I figured out a hackish way to do this that I'm a bit ashamed of. On Postgres 8.1:
SELECT generate_series, (SELECT username FROM users LIMIT 1 OFFSET generate_series) FROM generate_series(0,(SELECT count(*) - 1 FROM users));
I believe this technique will work even if your source table does not have unique ids or identifiers.
On SQL Server 2005 and higher, you can use OVER to accomplish this:
SELECT rank() over (order by company_id) as rownum
, company_name
FROM company