Get Value from XML attribute in SQL Server 2008 Using OPENXML - sql-server

I'm trying to extract a value from my XML and seem to be struggling. Hope someone can help
here is my XML
'<Transfer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Products>
<Product TUT="true" ID="38319223176264031724">
<Identifier>38319223176264031724</Identifier>
<ProductItemCode>83192</ProductItemCode>
<Qty>1</Qty>
<NetWeight>23.100</NetWeight>
<GrossWeight>23.684</GrossWeight>
<SerialNumber>317</SerialNumber>
<ECertItemNumber>2</ECertItemNumber>
<Markets Type="ECERT">
<Market>EU</Market>
<Market>US</Market>
</Markets>
<Attribute Name="PackDate">2016-09-20T00:00:00</Attribute>
<Attribute Name="PlantID">124</Attribute>
<Attribute Name="SlgrDate">2016-09-19T00:00:00</Attribute>
</Product>
<Product TUT="true" ID="28319219766306010024">
<Identifier>28319219766306010024</Identifier>
<ProductItemCode>83192</ProductItemCode>
<Qty>1</Qty>
<NetWeight>19.700</NetWeight>
<GrossWeight>20.284</GrossWeight>
<SerialNumber>100</SerialNumber>
<ECertItemNumber>2</ECertItemNumber>
<Markets Type="ECERT">
<Market>EU</Market>
<Market>US</Market>
</Markets>
<Attribute Name="PackDate">2016-11-01T00:00:00</Attribute>
<Attribute Name="PlantID">124</Attribute>
<Attribute Name="SlgrDate">2016-10-31T00:00:00</Attribute>
</Product>
</Products>
</Transfer>'
What I want to extract are the Identifier, ProductItemCode, NetWeight, GrossWeight, AND the Attribute Values of PackDate and SlgrDate.
I can easily get all of the fields EXCEPT for Attribute Values of PackDate and SlgrDate
Here is my code for the fields
if OBJECT_ID('tempdb..#XmlImportTest') is not null
drop table #XmlImportTest
CREATE TABLE #XmlImportTest(
xmlFileName VARCHAR(300) NOT NULL,
xml_data XML NOT NULL
)
GO
DECLARE #xmlFileName VARCHAR(3000)
SELECT #xmlFileName = 'K:\Upload\CSNXML\WaybillXml.xml'
--– dynamic sql is just so we can use #xmlFileName variable in OPENROWSET
EXEC('INSERT INTO #XmlImportTest(xmlFileName, xml_data)
SELECT ''' + #xmlFileName + ''', xmlData
FROM(
SELECT *
FROM OPENROWSET (BULK ''' + #xmlFileName + ''', SINGLE_BLOB) AS XMLDATA
) AS FileImport (XMLDATA)
')
GO
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
select #xml = (SELECT xml_data from #XmlImportTest)
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT Identifier as barcode,ProductItemCode as standpack,SerialNumber, NetWeight netwt_ind, GrossWeight grosswt_ind
FROM OPENXML (#hDoc, '/Transfer/Products/Product',2)
WITH (Identifier varchar(80),
ProductItemCode varchar(10),
SerialNumber varchar(48),
NetWeight decimal(13,2),
GrossWeight decimal(13,2)
)
exec sp_xml_removedocument #hDoc
the xml file contains the same as the sample xml provided
Now I have no idea how to get the value out of the Attributes for each product.
I am running this in SQL SERVER 2008

Use the optional ColPattern to specify the XPath to the node you want.
FROM OPENXML (#hDoc, '/Transfer/Products/Product',2)
WITH (
Identifier varchar(80),
ProductItemCode varchar(10),
SerialNumber varchar(48),
NetWeight decimal(13,2),
GrossWeight decimal(13,2),
PackDate datetime 'Attribute[#Name = "PackDate"]',
PlantID int 'Attribute[#Name = "PlantID"]',
SlgrDate datetime 'Attribute[#Name = "SlgrDate"]'
)

FROM OPENXML is outdated and should not be used anymore (rare exceptions exist)
Try to use the up-to-date XML-type-methods:
SELECT p.value(N'#TUT',N'bit') AS TUT
,p.value(N'#ID',N'nvarchar(max)') AS ID
,p.value(N'(Identifier/text())[1]',N'nvarchar(max)') AS Identifier
,p.value(N'(ProductItemCode/text())[1]',N'int') AS ProductItemCode
,p.value(N'(Qty/text())[1]',N'int') AS Qty
,p.value(N'(NetWeight/text())[1]',N'decimal(14,4)') AS NetWeight
,p.value(N'(SerialNumber/text())[1]',N'int') AS SerialNumber
,p.value(N'(ECertItemNumber/text())[1]',N'int') AS ECertItemNumber
,p.value(N'(Markets/#Type)[1]',N'nvarchar(max)') AS Markets_Type
,m.value(N'text()[1]',N'nvarchar(max)') AS Markets_Market
,p.value(N'(Attribute[#Name="PackDate"]/text())[1]',N'datetime') AS PackDate
,p.value(N'(Attribute[#Name="PlantID"]/text())[1]',N'int') AS PlantID
,p.value(N'(Attribute[#Name="SlgrDate"]/text())[1]',N'datetime') AS SlgrDate
FROM #xml.nodes(N'Transfer/Products/Product') AS A(p)
CROSS APPLY a.p.nodes(N'Markets/Market') AS B(m);
The result
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| TUT | ID | Identifier | ProductItemCode | Qty | NetWeight | SerialNumber | ECertItemNumber | Markets_Type | Markets_Market | PackDate | PlantID | SlgrDate |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 38319223176264031724 | 38319223176264031724 | 83192 | 1 | 23.1000 | 317 | 2 | ECERT | EU | 2016-09-20 00:00:00.000 | 124 | 2016-09-19 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 38319223176264031724 | 38319223176264031724 | 83192 | 1 | 23.1000 | 317 | 2 | ECERT | US | 2016-09-20 00:00:00.000 | 124 | 2016-09-19 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 28319219766306010024 | 28319219766306010024 | 83192 | 1 | 19.7000 | 100 | 2 | ECERT | EU | 2016-11-01 00:00:00.000 | 124 | 2016-10-31 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 28319219766306010024 | 28319219766306010024 | 83192 | 1 | 19.7000 | 100 | 2 | ECERT | US | 2016-11-01 00:00:00.000 | 124 | 2016-10-31 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
Hint: If you do not need the <Markets> as 1:n-relation (here doubling your resultset!), just remove the CROSS APPLY and the line starting with m.value.

Related

Get XML namespace text via SQL query

I am working with XML data on an SQL Server. The (exemplary) SQL looks as follows:
<Document xmlns="urn:iso:std:iso:20022:some:test:xmlns">
<Testnode>a</Testnode>
</Document>
The XML is available in a table with a column named <fata of type XML.
My question is: How can I create a SELECT query that shows the text of the namespace in one column?
The expected output should be:
+----------------------------------------+
| xmlns |
+----------------------------------------+
| urn:iso:std:iso:20022:some:test:xmlns |
+----------------------------------------+
The result column should be a character string (no XML).
So far I have tried this query, however the result is NULL:
SELECT Data.value('(./Document)[1]','nvarchar(max)') AS xmlns
FROM xmltable
You can try something along this:
DECLARE #xml XML=
N'<Document xmlns="urn:iso:std:iso:20022:some:test:xmlns">
<Testnode>a</Testnode>
</Document>';
--The XQuery-function namespace-uri() takes a singleton and returns its namespace uri
SELECT #xml.value('namespace-uri((/*:Document)[1])','nvarchar(max)');
As the <Document> element is living within the default namespace itself, we would have to know the namespace in advance in order to declare it. But - luckily - we can use the wildcard with *:.
Another option - one of the rare cases - is the usage of the outdated FROM OPENXML:
Try this:
DECLARE #xml XML=
N'<Document xmlns="urn:iso:std:iso:20022:some:test:xmlns">
<Testnode>a</Testnode>
</Document>';
DECLARE #docHandle INT;
EXEC sp_xml_preparedocument #docHandle OUTPUT, #xml;
SELECT * FROM OPENXML (#docHandle, '/*',1);
EXEC sp_xml_removedocument #docHandle;
This returns the complete XML with a lot of meta-data:
+----+----------+----------+-----------+--------+---------------------------------------+----------+------+---------------------------------------+
| id | parentid | nodetype | localname | prefix | namespaceuri | datatype | prev | text |
+----+----------+----------+-----------+--------+---------------------------------------+----------+------+---------------------------------------+
| 0 | NULL | 1 | Document | NULL | urn:iso:std:iso:20022:some:test:xmlns | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+---------------------------------------+----------+------+---------------------------------------+
| 2 | 0 | 2 | xmlns | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+---------------------------------------+----------+------+---------------------------------------+
| 4 | 2 | 3 | #text | NULL | NULL | NULL | NULL | urn:iso:std:iso:20022:some:test:xmlns |
+----+----------+----------+-----------+--------+---------------------------------------+----------+------+---------------------------------------+
| 3 | 0 | 1 | Testnode | NULL | urn:iso:std:iso:20022:some:test:xmlns | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+---------------------------------------+----------+------+---------------------------------------+
| 5 | 3 | 3 | #text | NULL | NULL | NULL | NULL | a |
+----+----------+----------+-----------+--------+---------------------------------------+----------+------+---------------------------------------+

SQL-Server XML-Bulk-Import and reading as table-data

I have the following Problem:
For the XML-Import into SQL-Sever, I use this code:
DROP TABLE XMLwithOpenXML
CREATE TABLE XMLwithOpenXML
(
Id INT IDENTITY PRIMARY KEY,
XMLData XML,
LoadedDateTime DATETIME
)
INSERT INTO XMLwithOpenXML(XMLData, LoadedDateTime)
SELECT CONVERT(XML, BulkColumn) AS BulkColumn, GETDATE()
FROM OPENROWSET(BULK '\\WINSER1\\proALPHA\\templates_eBus\\Test.xml', SINGLE_BLOB) AS x;
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SELECT #XML = XMLData FROM XMLwithOpenXML
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
It works fine. But here in the XML, I don't know, what should I do:
<MIME_INFO>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube105252.jpg</MIME_SOURCE>
<MIME_PURPOSE>normal</MIME_PURPOSE>
<MIME_ORDER>1</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>bbd372670.jpg</MIME_SOURCE>
<MIME_PURPOSE>logo</MIME_PURPOSE>
<MIME_ORDER>2</MIME_ORDER>
</MIME>
</MIME_INFO>
An user of me, needs both <MIME>-Blocks. But they are named the same!
How can I get this 8 rows contented in the 2 <Mime>-Tags? Rename is not the solution, because the XML has over 2.000.000 rows!
THX.
EDIT 16:20
Here the rest of the code above. With this tags it works fine:
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SELECT #XML = XMLData FROM XMLwithOpenXML
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT [SUPPLIER_AID]
,REFERENCE_FEATURE_SYSTEM_NAME
,REFERENCE_FEATURE_GROUP_ID
FROM OPENXML(#hDoc, 'BMECAT/T_NEW_CATALOG/ARTICLE')
--FROM OPENXML(#hDoc, 'BMECAT/T_NEW_CATALOG/ARTICLE/ARTICLE_ORDER_DETAILS')
--## Hier werden die gewünschten Columns deklariert.
WITH
(
SUPPLIER_AID [varchar](25) 'SUPPLIER_AID'
,REFERENCE_FEATURE_SYSTEM_NAME [varchar](25) 'REFERENCE_FEATURE_SYSTEM_NAME'
,REFERENCE_FEATURE_GROUP_ID [varchar](25) 'REFERENCE_FEATURE_GROUP_ID'
)
-----------------------------EDIT 16092016 / 08:14-----------------------------
I still don't understand your code, because you use not the real table "XMLwithOpenXML". Hier is one article of ~ 20.000 in the XML:
<BMECAT>
<T_NEW_CATALOG>
<ARTICLE mode="new">
<SUPPLIER_AID>9900026005</SUPPLIER_AID>
<MIME_INFO>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube105252.jpg</MIME_SOURCE>
<MIME_PURPOSE>normal</MIME_PURPOSE>
<MIME_ORDER>1</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>bbd372670.jpg</MIME_SOURCE>
<MIME_PURPOSE>logo</MIME_PURPOSE>
<MIME_ORDER>2</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube305149.jpg</MIME_SOURCE>
<MIME_PURPOSE>logo</MIME_PURPOSE>
<MIME_ORDER>3</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube108453.jpg</MIME_SOURCE>
<MIME_PURPOSE>others</MIME_PURPOSE>
<MIME_ORDER>4</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>application/pdf</MIME_TYPE>
<MIME_SOURCE>ube007100.pdf</MIME_SOURCE>
<MIME_PURPOSE>others</MIME_PURPOSE>
<MIME_ORDER>5</MIME_ORDER>
</MIME>
</MIME_INFO>
</ARTICLE>
</T_NEW_CATALOG>
</BMECAT>
You see there is one SUPPLIER_AID and four times a <MIME>-tag. I need only the first and the second (where normal and logo). What is in this case with the SUPPLIER_AID? I think the code has to look like:
WITH Numbered AS
(
SELECT LoadedDateTime
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS ID
--,a.query('.') AS SUPPLIER_AID
,m.query('.') AS mime
FROM XMLwithOpenXML AS t
CROSS APPLY t.XMLData.nodes('BMECAT/T_NEW_CATALOG/ARTICLE/MIME_INFO/MIME') AS A(m)
)
SELECT ID
--,[SUPPLIER_AID].value('(ARTICLE)[1]','nvarchar(max)') AS SUPPLIER_AID
,mime.value('(MIME/MIME_TYPE)[1]','nvarchar(max)') AS MIME_TYPE
,mime.value('(MIME/MIME_SOURCE)[1]','nvarchar(max)') AS MIME_SOURCE
,mime.value('(MIME/MIME_PURPOSE)[1]','nvarchar(max)') AS MIME_PURPOSE
,mime.value('(MIME/MIME_ORDER)[1]','nvarchar(max)') AS MIME_ORDER
FROM Numbered
With the new code, I get this:
+-----------+--------------+-------------+-----------+
|MIME_TYPE |MIME_SOURCE |MIME_PURPOSE |MIME_ORDER |
+-----------+--------------+-------------+-----------+
|image/jpeg |ube105252.jpg |normal |1 |
+-----------+--------------+-------------+-----------+
|image/jpeg |bbd372670.jpg |logo |2 |
+-----------+--------------+-------------+-----------+
|image/jpeg |ube105252.jpg |logo |3 |
+-----------+--------------+-------------+-----------+
|image/jpeg |bbd372670.jpg |others |4 |
+-----------+--------------+-------------+-----------+
|image/jpeg |bbd372670.jpg |others |5 |
+-----------+--------------+-------------+-----------+
But what I need is something like:
+-------------+------------+------------------+--------------+-------------+
|SUPPLIER_AID | MIME_TYPE | MIME_SOURCE | MIME_PURPOSE | MIME_ORDER |
+-------------+------------+------------------+--------------+-------------+
|9900026005 | image/jpeg | ube105252.jpg | normal | 1 |
+-------------+------------+------------------+--------------+-------------+
|9900026005 | image/jpeg | bbd372670.jpg | logo | 2 |
+-------------+------------+------------------+--------------+-------------+
Your approach with FROM OPENXML is outdated and should not be used any more. There are much better XML methods like .node(), .value(), .query() and .modify().
The way you get the XML into your table is quite OK. Once you have it there, you should continue like this:
Attention I use a declared mock-up-table to simulate your table.
DECLARE #XMLwithOpenXML TABLE(XMLData XML,LoadedDateTime DATETIME);
INSERT INTO #XMLwithOpenXML VALUES
('<MIME_INFO>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube105252.jpg</MIME_SOURCE>
<MIME_PURPOSE>normal</MIME_PURPOSE>
<MIME_ORDER>1</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>bbd372670.jpg</MIME_SOURCE>
<MIME_PURPOSE>logo</MIME_PURPOSE>
<MIME_ORDER>2</MIME_ORDER>
</MIME>
</MIME_INFO>',GETDATE());
At this point, your XML is successfully taken into your table
The CTE "Numbered" will read all MIME elements using .nodes() in there inherent order and number them accordingly.
The SELECT pulls the actual data
WITH Numbered AS
(
SELECT LoadedDateTime
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS ID
,m.query('.') AS mime
FROM #XMLwithOpenXML AS t
CROSS APPLY t.XMLData.nodes('/MIME_INFO/MIME') AS A(m)
)
SELECT ID
,LoadedDateTime
,mime.value('(MIME/MIME_TYPE)[1]','nvarchar(max)') AS MIME_TYPE
,mime.value('(MIME/MIME_SOURCE)[1]','nvarchar(max)') AS MIME_SOURCE
,mime.value('(MIME/MIME_PURPOSE)[1]','nvarchar(max)') AS MIME_PURPOSE
,mime.value('(MIME/MIME_ORDER)[1]','nvarchar(max)') AS MIME_ORDER
FROM Numbered
The result
+----+-------------------------+------------+---------------+--------------+------------+
| ID | LoadedDateTime | MIME_TYPE | MIME_SOURCE | MIME_PURPOSE | MIME_ORDER |
+----+-------------------------+------------+---------------+--------------+------------+
| 1 | 2016-09-15 16:37:30.730 | image/jpeg | ube105252.jpg | normal | 1 |
+----+-------------------------+------------+---------------+--------------+------------+
| 2 | 2016-09-15 16:37:30.730 | image/jpeg | bbd372670.jpg | logo | 2 |
+----+-------------------------+------------+---------------+--------------+------------+
UPDATE
You did not show the full XML... With the example given above this code extracts all you might want to knwo:
WITH Numbered AS
(
SELECT Id
,LoadedDateTime
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS MimeRowNr
,a.value('#mode','nvarchar(max)') ARTICLE_MODE
,a.value('SUPPLIER_AID[1]','nvarchar(max)') AS SUPPLIER_AID
,m.query('.') AS mime
FROM XMLwithOpenXML AS t
CROSS APPLY t.XMLData.nodes('/BMECAT/T_NEW_CATALOG/ARTICLE') AS A(a)
CROSS APPLY a.nodes('MIME_INFO/MIME') AS B(m)
)
SELECT Id
,MimeRowNr
,LoadedDateTime
,ARTICLE_MODE
,SUPPLIER_AID
,mime.value('(MIME/MIME_TYPE)[1]','nvarchar(max)') AS MIME_TYPE
,mime.value('(MIME/MIME_SOURCE)[1]','nvarchar(max)') AS MIME_SOURCE
,mime.value('(MIME/MIME_PURPOSE)[1]','nvarchar(max)') AS MIME_PURPOSE
,mime.value('(MIME/MIME_ORDER)[1]','int') AS MIME_ORDER
FROM Numbered;
The result
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| Id | MimeRowNr | LoadedDateTime | ARTICLE_MODE | SUPPLIER_AID | MIME_TYPE | MIME_SOURCE | MIME_PURPOSE | MIME_ORDER |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 1 | 2016-09-16 09:32:53.570 | new | 9900026005 | image/jpeg | ube105252.jpg | normal | 1 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 2 | 2016-09-16 09:32:53.570 | new | 9900026005 | image/jpeg | bbd372670.jpg | logo | 2 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 3 | 2016-09-16 09:32:53.570 | new | 9900026005 | image/jpeg | ube305149.jpg | logo | 3 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 4 | 2016-09-16 09:32:53.570 | new | 9900026005 | image/jpeg | ube108453.jpg | others | 4 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 5 | 2016-09-16 09:32:53.570 | new | 9900026005 | application/pdf | ube007100.pdf | others | 5 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+

Generating unique identifiers as a set based query

I am moving a bunch of code over from entirely cursor based to set based and generating this has been doing my head in. We create a 6 character shortcode (unique) for each company inserted into the database and I (want) to achieve this outside of a cursor.
Example of where I am at so far:
CREATE TABLE #customers (name VARCHAR(50), shortname VARCHAR(10))
INSERT INTO #customers VALUES
('Michael Smith', 'Michae')
,('Michael Douglas', 'Mich_1')
,('Michael Yang', 'Mich_2')
CREATE TABLE #newcustomers (name VARCHAR(50), shortname VARCHAR(10) NULL)
INSERT INTO #newcustomers (name) VALUES
('Michael Black')
,('Michael White')
SELECT * FROM #customers
SELECT * FROM #newcustomers
DECLARE #shortname VARCHAR(10)
DECLARE #iteration INT = 0
WHILE EXISTS(SELECT shortname FROM #customers WHERE shortname = #shortname)
BEGIN
SELECT #shortname = LEFT(name, 6) FROM #newcustomers
UPDATE #newcustomers SET shortname = #shortname
SET #shortname = LEFT(#shortname, 4) + '_' + #iteration
SET #iteration = #iteration + 1
END
Hopefully the example is sufficient in identifying where I am trying to get to, any suggestions or examples would be very helpful. My example does not work.
Try this
Your table as mock-up
CREATE TABLE #customers (ID INT IDENTITY, name VARCHAR(50), shortname VARCHAR(10))
INSERT INTO #customers VALUES
('Michael Smith', 'Michae')
,('Michael Douglas', 'Mich_1')
,('Michael Yang', 'Mich_3')
,('Testman', 'Testma')
,('Testman1', 'Test_1');
CREATE TABLE #newcustomers (ID INT IDENTITY,name VARCHAR(50), shortname VARCHAR(10) NULL)
INSERT INTO #newcustomers (name) VALUES
('Michael Black')
,('Michael White')
,('Testman2')
,('Someone new');
--This CTE will combine all existing names
WITH AllNames AS
(
SELECT '1_old' AS datasource,ID,name,shortname FROM #customers
UNION ALL SELECT '2_new',ID,name,shortname FROM #newcustomers
)
--This CTE will use the combined list and calculate the right "index"
,ShortNames AS
(
SELECT c.*
,A.First6
,ROW_NUMBER() OVER(PARTITION BY A.First6 ORDER BY datasource,ID) AS NrTotal
,ROW_NUMBER() OVER(PARTITION BY datasource,A.First6 ORDER BY datasource,ID) AS Nr
,CASE WHEN ISNUMERIC(SUBSTRING(shortname+' ',6,10))=1
THEN CAST(SUBSTRING(shortname+' ',6,10) AS INT) ELSE 0 END AS ExistIndex
FROM AllNames AS c
CROSS APPLY(SELECT LEFT(name + ' ',6)) AS A(First6)
)
--All new with NrTotal=1 get the 6 letters as is, all other get the index
SELECT *
,CASE WHEN datasource='1_old' THEN shortname ELSE
CASE WHEN datasource='2_new' AND NrTotal=1 THEN First6
ELSE LEFT(First6,4) + '_' + CAST(Nr + (SELECT ISNULL(MAX(x.ExistIndex),1)
FROM ShortNames AS x
WHERE x.First6=ShortNames.First6) AS VARCHAR(5))
END
END
FROM ShortNames
GO
DROP TABLE #customers;
DROP TABLE #newcustomers;
The result
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| datasource | ID | name | shortname | First6 | NrTotal | Nr | ExistIndex | (Kein Spaltenname) |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 1_old | 1 | Michael Smith | Michae | Michae | 1 | 1 | 0 | Michae |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 1_old | 2 | Michael Douglas | Mich_1 | Michae | 2 | 2 | 1 | Mich_1 |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 1_old | 3 | Michael Yang | Mich_3 | Michae | 3 | 3 | 3 | Mich_3 |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 1_old | 4 | Testman | Testma | Testma | 1 | 1 | 0 | Testma |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 1_old | 5 | Testman1 | Test_1 | Testma | 2 | 2 | 1 | Test_1 |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 2_new | 1 | Michael Black | NULL | Michae | 4 | 1 | 0 | Mich_4 |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 2_new | 2 | Michael White | NULL | Michae | 5 | 2 | 0 | Mich_5 |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 2_new | 4 | Someone new | NULL | Someon | 1 | 1 | 0 | Someon |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 2_new | 3 | Testman2 | NULL | Testma | 3 | 1 | 0 | Test_2 |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
One option is to use a computed column.
A table design along these lines would work:
- Sample table.
DECLARE #Sample TABLE
(
Id INT IDENTITY(1, 1),
FullName VARCHAR(255),
ShortName AS LEFT(FullName, 4) + '_' + CAST(Id AS VARCHAR(255))
)
;
-- Add set containing two companies.
INSERT INTO #Sample
(
FullName
)
VALUES
('ABC LTD'),
('XYZ PLC')
;
Returns
Id FullName ShortName
1 ABC LTD ABC _1
2 XYZ PLC XYZ _1
The Id and ShortName columns will be managed by SQL Server. You only need to add the FullName.
EDIT
Reworked example using table variable, to make it easier to play along.

SQL sum by weekly buckets

This is similar to some other questions on here however not close enough that I have all the info to do it myself. I want to pivot a date range with ability to limit by year. I'm not sure how they want to limit the data at the moment, maybe a year previous to a year forward for now.
I want the start day of the week to be Monday and end to be Sunday. Any quantities to fall between these days to be summed for the week per reftype with the date displaying as that starting Monday.
I have data below.
+---------+---------+------------------+-------------------------+------------------------+
| Itemid | RefType | name | OriginalReqDate | Qty |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 8 | Purchase order | 2016-03-04 00:00:00.000 | 2346.0000000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 12 | Production order | 2016-03-04 00:00:00.000 | -1295.4000000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 12 | Production order | 2016-03-07 00:00:00.000 | -3651.6000000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 8 | Purchase order | 2016-03-11 00:00:00.000 | 4692.0000000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 12 | Production order | 2016-03-14 00:00:00.000 | -1397.4000000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 12 | Production order | 2016-03-21 00:00:00.000 | -958.8000000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 45 | Formula line | 2016-03-28 00:00:00.000 | -696.1700000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 45 | Formula line | 2016-04-03 00:00:00.000 | -527.5500000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 8 | Purchase order | 2016-04-07 00:00:00.000 | 7038.0000000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 45 | Formula line | 2016-04-07 00:00:00.000 | -1186.5500000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
I would like output as
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
| ItemId | RefType | Name | 2016-03-04 | 2016-03-11 | 2016-03-18 | 2016-03-25 | 2016-04-01 |
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
| B406227 | 1 | On-hand | 470.7600000000000000 | NULL | NULL | NULL | NULL |
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
| B406227 | 8 | Purchase order | 2346.0000000000000000 | 4692.0000000000000000 | NULL | NULL | NULL |
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
| B406227 | 12 | Production order | -1295.4000000000000000 | -3651.6000000000000000 | -1397.4000000000000000 | -958.8000000000000000 | NULL |
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
| B406227 | 33 | Planned purchase order | NULL | NULL | NULL | NULL | NULL |
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
| B406227 | 45 | Formula line | NULL | NULL | NULL | NULL | -696.1700000000000000 |
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
| B406227 | 99 | Total for B406227 | 1992.1200000000000000 | 2561.7600000000000000 | 1164.3600000000000000 | 205.5600000000000000 | -490.6100000000000000 |
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
This is my attempt:
IF OBJECT_ID('tt', 'U') IS NOT NULL
DROP TABLE tt;
DECLARE #Columns NVARCHAR(MAX);
DECLARE #SQL NVARCHAR(MAX);
SELECT #Columns = COALESCE(#Columns + ',', '') + QUOTENAME(ReqDate)
FROM ( SELECT DISTINCT
CAST(ReqDate AS DATE) AS ReqDate
FROM SourceTable
) AS A
ORDER BY A.ReqDate;
SET #SQL = 'WITH PivotData AS (SELECT DataAreaId, ItemId, RefType, Name, ReqDate, Qty
FROM SourceTable)
SELECT DataAreaId, ItemId, RefType, Name ' + #Columns + '
INTO tt
FROM PivotData
PIVOT
(SUM(Qty)
FOR ReqDate IN (' + #Columns + ')
) AS PivotResult ORDER BY DataAreaId, ItemId, RefType';
EXEC (#SQL);
IF OBJECT_ID('adhoc.V_tt', 'V') IS NOT NULL
DROP VIEW adhoc.V_tt;
GO
CREATE VIEW adhoc.V_tt
AS
( SELECT *
FROM tt
);
EDIT: Dynamic SQL
With this you would get the column headers dynamically pointing on Sundays...
Attention: Depending on your system's culture you might want to have a look on SET DATEFIRST and ##DATEFIRST...
CREATE TABLE #TestTbl(Itemid VARCHAR(100),RefType INT,name VARCHAR(100),OriginalReqDate DATETIME,Qty DECIMAL(8,2));
INSERT INTO #TestTbl VALUES
('B406227',8,'Purchase order','2016-03-04T00:00:00.000',2346.0000000000000000)
,('B406227',12,'Production order','2016-03-04T00:00:00.000',-1295.4000000000000000)
,('B406227',12,'Production order','2016-03-07T00:00:00.000',-3651.6000000000000000)
,('B406227',8,'Purchase order','2016-03-11T00:00:00.000',4692.0000000000000000)
,('B406227',12,'Production order','2016-03-14T00:00:00.000',-1397.4000000000000000)
,('B406227',12,'Production order','2016-03-21T00:00:00.000',-958.8000000000000000)
,('B406227',45,'Formula line','2016-03-28T00:00:00.000',-696.1700000000000000)
,('B406227',45,'Formula line','2016-04-03T00:00:00.000',-527.5500000000000000)
,('B406227',8,'Purchase order','2016-04-07T00:00:00.000',7038.0000000000000000)
,('B406227',45,'Formula line','2016-04-07T00:00:00.000',-1186.5500000000000000);
DECLARE #colNames VARCHAR(MAX)=
STUFF
(
(
SELECT DISTINCT ',[' + CONVERT(VARCHAR(10),DATEADD(DAY,DATEPART(DW,OriginalReqDate) * (-1),OriginalReqDate),120) + ']'
FROM #TestTbl
FOR XML PATH('')
),1,1,''
);
DECLARE #cmd VARCHAR(MAX)=
'
SELECT p.*
FROM
(
SELECT tt.Itemid
,tt.RefType
,tt.name
,SUM(Qty) AS SumQty
,CONVERT(VARCHAR(10),DATEADD(DAY,DATEPART(DW,OriginalReqDate) * (-1),OriginalReqDate),120) AS ColumName
FROM #TestTbl AS tt
GROUP BY ItemId,RefType,name,CONVERT(VARCHAR(10),DATEADD(DAY,DATEPART(DW,OriginalReqDate) * (-1),OriginalReqDate),120)
) AS tbl
PIVOT
(
SUM(SumQty) FOR ColumName IN(' + #colNames + ')
) AS p
';
EXEC (#cmd);
DROP TABLE #TestTbl;
The result:
Itemid RefType name 2016-02-28 2016-03-06 2016-03-13 2016-03-20 2016-03-27 2016-04-03
B406227 8 Purchase order 2346.00 4692.00 NULL NULL NULL 7038.00
B406227 12 Production order -1295.40 -3651.60 -1397.40 -958.80 NULL NULL
B406227 45 Formula line NULL NULL NULL NULL -1223.72 -1186.55
Previous
This is a hard coded approach for the given sample data. If you want your columns to get a caption like 2016-03-04 you might think about dynamic SQL or you create the columnName (and the IN() list) for the correct output.
CREATE TABLE #TestTbl(Itemid VARCHAR(100),RefType INT,name VARCHAR(100),OriginalReqDate DATETIME,Qty DECIMAL(8,2));
INSERT INTO #TestTbl VALUES
('B406227',8,'Purchase order','2016-03-04T00:00:00.000',2346.0000000000000000)
,('B406227',12,'Production order','2016-03-04T00:00:00.000',-1295.4000000000000000)
,('B406227',12,'Production order','2016-03-07T00:00:00.000',-3651.6000000000000000)
,('B406227',8,'Purchase order','2016-03-11T00:00:00.000',4692.0000000000000000)
,('B406227',12,'Production order','2016-03-14T00:00:00.000',-1397.4000000000000000)
,('B406227',12,'Production order','2016-03-21T00:00:00.000',-958.8000000000000000)
,('B406227',45,'Formula line','2016-03-28T00:00:00.000',-696.1700000000000000)
,('B406227',45,'Formula line','2016-04-03T00:00:00.000',-527.5500000000000000)
,('B406227',8,'Purchase order','2016-04-07T00:00:00.000',7038.0000000000000000)
,('B406227',45,'Formula line','2016-04-07T00:00:00.000',-1186.5500000000000000);
SELECT p.*
FROM
(
SELECT tt.Itemid
,tt.RefType
,tt.name
,SUM(Qty) AS SumQty
,'w' + CAST(DATEPART(WEEK,OriginalReqDate) AS VARCHAR(MAX)) AS ColumName
FROM #TestTbl AS tt
GROUP BY ItemId,RefType,name,DATEPART(WEEK,OriginalReqDate)
) AS tbl
PIVOT
(
SUM(SumQty) FOR ColumName IN(w10,w11,w12,w13,w14,w15)
) AS p
DROP TABLE #TestTbl;
The result:
Itemid RefType name w10 w11 w12 w13 w14 w15
B406227 8 Purchase order 2346.00 4692.00 NULL NULL NULL 7038.00
B406227 12 Production order -1295.40 -3651.60 -1397.40 -958.80 NULL NULL
B406227 45 Formula line NULL NULL NULL NULL -1223.72 -1186.55
This is my attempt...
IF OBJECT_ID('tt', 'U') IS NOT NULL
DROP TABLE tt;
DECLARE #Columns NVARCHAR(MAX);
DECLARE #SQL NVARCHAR(MAX);
SELECT #Columns = COALESCE(#Columns + ',', '') + QUOTENAME(ReqDate)
FROM ( SELECT DISTINCT
CAST(ReqDate AS DATE) AS ReqDate
FROM SourceTable
) AS A
ORDER BY A.ReqDate;
SET #SQL = 'WITH PivotData AS (SELECT DataAreaId, ItemId, RefType, Name, ReqDate, Qty
FROM SourceTable)
SELECT DataAreaId, ItemId, RefType, Name ' + #Columns + '
INTO tt
FROM PivotData
PIVOT
(SUM(Qty)
FOR ReqDate IN (' + #Columns + ')
) AS PivotResult ORDER BY DataAreaId, ItemId, RefType';
EXEC (#SQL);
IF OBJECT_ID('adhoc.V_tt', 'V') IS NOT NULL
DROP VIEW adhoc.V_tt;
GO
CREATE VIEW adhoc.V_tt
AS
( SELECT *
FROM tt
);

T-SQL Transposing column headers into Rows

I want to transpose my table.
I have simple 'Person' table as shown below.
+---+----------+------------+------------+----------------------+
|ID | Person | BirthDate | Phone | Email |
+---+----------+------------+------------+----------------------+
| 1 | Tom | 1985-11-08 | 1111111111 | tom#somedomain.com |
+---+----------+------------+------------+----------------------+
| 2 | Dick | 1982-02-24 | 2222222222 | dick#otherdomain.com |
+---+----------+------------+------------+----------------------+
| 3 | Harry | 1986-04-17 | 3333333333 | harry#thatdomain.com |
+---+----------+------------+------------+----------------------+
And I want this table to be transposed like below.
+-----------+--------------------+----------------------+----------------------+
| Key | Value1 | Value2 | Value3 |
+-----------+--------------------+----------------------+----------------------+
| ID | 1 | 2 | 3 |
+-----------+--------------------+----------------------+----------------------+
| Person | Tom | Dick | Harry |
+-----------+--------------------+----------------------+----------------------+
| BirthDate | 1985-11-08 | 1982-02-24 | 1986-04-17 |
+-----------+--------------------+----------------------+----------------------+
| Phone | 1111111111 | 2222222222 | 3333333333 |
+-----------+--------------------+----------------------+----------------------+
| Email | tom#somedomain.com | dick#otherdomain.com | harry#thatdomain.com |
+-----------+--------------------+----------------------+----------------------+
I am using MS SQL server 2008 R2.
Try this.. First u need to unpivot the columns using Cross apply to get the data in single row. Then pivot that row to get the result.
CREATE TABLE #tt
(ID INT,Person VARCHAR(50),BirthDate DATE,
Phone BIGINT,Email VARCHAR(50)
)
INSERT INTO #tt
VALUES (1,'Tom','1985-11-08',1111111111,'tom#somedomain.com' ),
( 2,'Dick','1982-02-24',2222222222,'dick#otherdomain.com'),
( 3,'Harry ','1986-04-17',3333333333,'harry#thatdomain.com' )
SELECT [key],
Max([value1]) [value1],
Max([value2]) [value2],
Max([value3]) [value3]
FROM (SELECT 'value' + CONVERT(VARCHAR(30), id) valued,
*
FROM #tt
CROSS apply (VALUES ('ID',
CONVERT(VARCHAR(50), ID)),
('Person',Person),
('BirthDate',CONVERT(VARCHAR(50), BirthDate)),
('Phone',CONVERT(VARCHAR(50), Phone)),
('Email',Email)) cp ([key], data))a
PIVOT (Max(data)
FOR valued IN([value1],[value2],[value3])) piv
GROUP BY [key]
DYNAMIC VERSION
Declare #cols varchar(max)='',#aggcols varchar(max)='',#sql nvarchar(max)
SELECT #cols+= ',value' + CONVERT(VARCHAR(30), id)
FROM #tt
SELECT #aggcols+= ',max([value' + CONVERT(VARCHAR(30), id) +']) value' + CONVERT(VARCHAR(30), id)
FROM #tt
select #cols= right(#cols,LEN(#cols)-1)
select #aggcols =right(#aggcols,LEN(#aggcols)-1)
set #sql = 'SELECT [key],
'+#aggcols+'
FROM (SELECT ''value'' + CONVERT(VARCHAR(30), id) valued,
*
FROM #tt
CROSS apply (VALUES (''ID'',CONVERT(VARCHAR(50), ID)),
(''Person'',Person),
(''BirthDate'',CONVERT(VARCHAR(50), BirthDate)),
(''Phone'',CONVERT(VARCHAR(50), Phone)),
(''Email'',Email)) cp ([key], data))a
PIVOT (Max(data)
FOR valued IN('+#cols+')) piv
GROUP BY [key] '
execute sp_executesql #sql
OUTPUT
+----------+--------------------+---------------------+----------------------+
|key | value1 | value2 | value3 |
+----------+--------------------+---------------------+----------------------+
|BirthDate | 1985-11-08 | 1982-02-24 | 1986-04-17 |
+----------+--------------------+---------------------+----------------------+
|Email | tom#somedomain.com |dick#otherdomain.com | harry#thatdomain.com |
+----------+--------------------+---------------------+----------------------+
|ID | 1 | 2 | 3 |
+----------+--------------------+---------------------+----------------------+
|Person | Tom | Dick | Harry |
+----------+--------------------+---------------------+----------------------+
|Phone | 1111111111 | 2222222222 | 3333333333 |
+----------+--------------------+---------------------+----------------------+

Resources