How to combine two FOR XML AUTO into 1 XML? - sql-server

We are using SQL Server 2012.
Table myTbl has a one to many relationship to table myAllocation
Table ABC_myTbl has a one to many relationship to table ABC_myAllocation
The below query combined 2 FOR XML AUTO into 1 XML, but the problem is ID, SystemSource, Manager are included in element TradeTicket instead of on their own, and accountManager, unitPrice are included in element allocationRow instead of on their own.
Thank you
SELECT '<?xml version="1.0"?>'+
(SELECT
( SELECT trTicket.[id],trTicket.[manager],'PFM' as SystemSource
,allocationRow.accountNumber,allocationRow.unitPrice
FROM myTbl AS trTicket
LEFT JOIN myAllocation AS allocationRow ON allocationRow.trade_ticket_id=trTicket.id
WHERE trTicket.ID = 8779631
ORDER BY trTicket.id,allocationRow.AccountNumber
FOR XML AUTO, type)
,
(
SELECT trTicket.[id],trTicket.[manager],'CRD' as SystemSource
,allocationRow.accountNumber,allocationRow.unitPrice
FROM ABC_myTbl AS trTicket
LEFT JOIN ABC_myAllocation AS allocationRow ON allocationRow.trade_ticket_id=trTicket.id
WHERE trTicket.ID = 8
ORDER BY trTicket.id,allocationRow.AccountNumber
FOR XML AUTO, type)
FOR XML PATH('trTickets'), ELEMENTS) AS XMLResult
This is the current result:
<?xml version="1.0"?>
<trTickets>
<trTicket id="8779631" SystemSource="PFM" manager="MCM">
<allocationRow accountNumber="292 " unit_Price="300"/>
</trTicket>
<trTicket id="8" SystemSource="CRD" manager="DOYLE">
<allocationRow unitPrice="100" accountNumber="F11 "/>
<allocationRow unitPrice="200" accountNumber="F22 "/>
</trTicket>
</trTickets>
This is the desired result that I am looking for:
<?xml version="1.0"?>
<trTickets>
<trTicket>
<id>8</id>
<manager>DOYLE</manager>
<SystemSource>CRD</SystemSource>
<allocationRow>
<accountNumber>F11</accountNumber>
<unitPrice>100</unitPrice>
</allocationRow>
<allocationRow>
<accountNumber>F22</accountNumber>
<unitPrice>200</unitPrice>
</allocationRow>
</trTicket>
<trTicket>
<id>8779631</id>
<manager>MCM</manager>
<SystemSource>PFM</SystemSource>
<allocationRow>
<accountNumber>292</accountNumber>
<unitPrice>300</unitPrice>
</allocationRow>
</trTicket>
</trTickets>
Data sample:
Table ABC_myTbl:
ID Manager
-----------
8 DOYLE
Table ABC_myAllocation:
accountNumber unitPrice
-------------------------
F11 100
F22 200
Table myTbl:
ID Manager
---------------
8779631 MCM
Table myAllocation:
accountNumber unitPrice
--------------------------
292 300
DDL for the tables and their data:
CREATE TABLE dbo.ABC_myTbl
(
ID INT NOT NULL,
MANAGER VARCHAR(10) NOT NULL
)
CREATE TABLE dbo.myTbl
(
ID INT NOT NULL,
MANAGER VARCHAR(10) NOT NULL
)
CREATE TABLE dbo.ABC_myAllocation
(
accountNumber VARCHAR(10) NOT NULL,
unitprice NUMERIC(10, 3) NOT NULL
)
CREATE TABLE dbo.myAllocation
(
accountNumber VARCHAR(10) NOT NULL,
unitprice NUMERIC(10, 3) NOT NULL
)
INSERT INTO dbo.ABC_myTbl VALUES (8,'DOYLE')
INSERT INTO dbo.ABC_myAllocation VALUES ('F11',100)
INSERT INTO dbo.ABC_myAllocation VALUES ('F22',200)
INSERT INTO dbo.myTbl VALUES (8779631,'MCM')
INSERT INTO dbo.myAllocation VALUES ('292',300)

I didn't wait for your DDL and sample data population. So I created a conceptual sample for you. Please pay attention that the tables have implied relationships and they are used in the WHERE clauses.
SQL
-- DDL and sample data population, start
DECLARE #tbl1 TABLE (ID INT PRIMARY KEY, Manager VARCHAR(20));
INSERT INTO #tbl1 (ID, Manager) VALUES
(8, 'DOYLE'),
(9, 'XYZ');
DECLARE #tbl1Child TABLE (accountNumber CHAR(3) PRIMARY KEY, ParentID INT, unitPrice DECIMAL(10,2));
INSERT INTO #tbl1Child (accountNumber, ParentID, unitPrice) VALUES
('F11', 8, 100)
,('F22', 8, 200)
,('F70', 9, 770);
DECLARE #tbl2 TABLE (ID INT PRIMARY KEY, Manager VARCHAR(20));
INSERT INTO #tbl2 (ID, Manager) VALUES
(8779631, 'MCM')
,(8779555, 'TTT');
DECLARE #tbl2Child TABLE (accountNumber CHAR(3) PRIMARY KEY, ParentID INT, unitPrice DECIMAL(10,2));
INSERT INTO #tbl2Child (accountNumber, ParentID, unitPrice) VALUES
('292', 8779631, 300)
,('255', 8779555, 500);
-- DDL and sample data population, end
SELECT TOP(1) NULL
, (
SELECT *
, (
SELECT * FROM #tbl1Child AS c
WHERE p.ID = c.ParentID
FOR XML PATH('allocation_row'), TYPE
)
FROM #tbl1 AS p
FOR XML PATH('tradeTicket'), TYPE
)
, (
SELECT *
, (
SELECT * FROM #tbl2Child AS c
WHERE p.ID = c.ParentID
FOR XML PATH('allocation_row'), TYPE
)
FROM #tbl2 AS p
FOR XML PATH('tradeTicket'), TYPE
)
FROM #tbl1
FOR XML PATH(''), TYPE, ROOT('tradeTickets');
Output
<tradeTickets>
<tradeTicket>
<ID>8</ID>
<Manager>DOYLE</Manager>
<allocation_row>
<accountNumber>F11</accountNumber>
<ParentID>8</ParentID>
<unitPrice>100.00</unitPrice>
</allocation_row>
<allocation_row>
<accountNumber>F22</accountNumber>
<ParentID>8</ParentID>
<unitPrice>200.00</unitPrice>
</allocation_row>
</tradeTicket>
<tradeTicket>
<ID>9</ID>
<Manager>XYZ</Manager>
<allocation_row>
<accountNumber>F70</accountNumber>
<ParentID>9</ParentID>
<unitPrice>770.00</unitPrice>
</allocation_row>
</tradeTicket>
<tradeTicket>
<ID>8779555</ID>
<Manager>TTT</Manager>
<allocation_row>
<accountNumber>255</accountNumber>
<ParentID>8779555</ParentID>
<unitPrice>500.00</unitPrice>
</allocation_row>
</tradeTicket>
<tradeTicket>
<ID>8779631</ID>
<Manager>MCM</Manager>
<allocation_row>
<accountNumber>292</accountNumber>
<ParentID>8779631</ParentID>
<unitPrice>300.00</unitPrice>
</allocation_row>
</tradeTicket>
</tradeTickets>

Related

How to get records which has more than one entries on another table

An example scenario for my question would be:
How to get all persons who has multiple address types?
Now here's my sample data:
CREATE TABLE #tmp_1 (
ID uniqueidentifier PRIMARY KEY
, FirstName nvarchar(max)
, LastName nvarchar(max)
)
CREATE TABLE #tmp_2 (
SeedID uniqueidentifier PRIMARY KEY
, SomeIrrelevantCol nvarchar(max)
)
CREATE TABLE #tmp_3 (
KeyID uniqueidentifier PRIMARY KEY
, ID uniqueidentifier REFERENCES #tmp_1(ID)
, SeedID uniqueidentifier REFERENCES #tmp_2(SeedID)
, SomeIrrelevantCol nvarchar(max)
)
INSERT INTO #tmp_1
VALUES
('08781F73-A06B-4316-B6A5-802ED58E54BE', 'AAAAAAA', 'aaaaaaa'),
('4EC71FCE-997C-46AA-B119-6C5A2545DDC2', 'BBBBBBB', 'bbbbbbb'),
('B0726ABF-738E-48BC-95CB-091C9D731A0E', 'CCCCCCC', 'ccccccc'),
('6C6CE284-A63C-49D2-B2CC-F25C9CBC8FB8', 'DDDDDDD', 'ddddddd')
INSERT INTO #tmp_2
VALUES
('4D10B4EC-C929-4D6B-8C94-11B680CF2221', 'Value1'),
('4C891FE9-60B6-41BE-A64B-11A9A8B58AB2', 'Value2'),
('6F6EFED6-8EA0-4F70-A63F-6A103D0A71BD', 'Value3')
INSERT INTO #tmp_3
VALUES
(NEWID(), '08781F73-A06B-4316-B6A5-802ED58E54BE', '4D10B4EC-C929-4D6B-8C94-11B680CF2221', 'sdfsdgdfbgcv'),
(NEWID(), '08781F73-A06B-4316-B6A5-802ED58E54BE', '4C891FE9-60B6-41BE-A64B-11A9A8B58AB2', 'asdfadsas'),
(NEWID(), '08781F73-A06B-4316-B6A5-802ED58E54BE', '4C891FE9-60B6-41BE-A64B-11A9A8B58AB2', 'xxxxxeeeeee'),
(NEWID(), '4EC71FCE-997C-46AA-B119-6C5A2545DDC2', '4D10B4EC-C929-4D6B-8C94-11B680CF2221', 'sdfsdfsd'),
(NEWID(), 'B0726ABF-738E-48BC-95CB-091C9D731A0E', '4D10B4EC-C929-4D6B-8C94-11B680CF2221', 'zxczxcz'),
(NEWID(), 'B0726ABF-738E-48BC-95CB-091C9D731A0E', '6F6EFED6-8EA0-4F70-A63F-6A103D0A71BD', 'eerwerwe'),
(NEWID(), '6C6CE284-A63C-49D2-B2CC-F25C9CBC8FB8', '4D10B4EC-C929-4D6B-8C94-11B680CF2221', 'vbcvbcvbcv')
Which gives you:
This is my attempt:
SELECT
t1.*
, Cnt -- not really needed. Just added for visual purposes
FROM #tmp_1 t1
LEFT JOIN (
SELECT
xt.ID
, COUNT(1) Cnt
FROM (
SELECT
#tmp_3.ID
, COUNT(1) as Cnt
FROM #tmp_3
GROUP BY ID, SeedID
) xt
GROUP BY ID
) t2
ON t1.ID = t2.ID
WHERE t2.Cnt > 1
Which gives:
ID FirstName LastName Cnt
B0726ABF-738E-48BC-95CB-091C9D731A0E CCCCCCC ccccccc 2
08781F73-A06B-4316-B6A5-802ED58E54BE AAAAAAA aaaaaaa 2
Although this gives me the correct results, I'm afraid that this query is not the right way to do this performance-wise because of the inner queries. Any input is very much appreciated.
NOTE:
A person can have multiple address of the same address types.
"Person-Address" is not the exact use-case. This is just an example.
The Cnt column is not really needed in the result set.
The way you have named your sample tables and data help little in understanding the problem.
I think you want all IDs which have 2 or more SomeIrrelevantCol values in the last table?
This can be done by:
select * from #tmp_1
where ID in
(
select ID
from #tmp_3
group by ID
having count(distinct SomeIrrelevantCol)>=2
)

Cascading data insert

I have these 3 tables:
CREATE TABLE tblPrimary(
Id INT IDENTITY(1,1) NOT NULL,
SampleID VARCHAR(8)
PRIMARY KEY (Id)
)
CREATE TABLE tblSecondary(
PrimaryId INT NOT NULL,
SampleName VARCHAR(50) NULL
)
CREATE TABLE tblSample(
SampleId VARCHAR(8) NOT NULL,
Name VARCHAR(50) NULL
PRIMARY KEY (SampleId)
)
Some sample data for tblSample
INSERT INTO tblSample VALUES ('A-1101', 'The CP 1014')
INSERT INTO tblSample VALUES ('A-1102', 'The NT 1014')
INSERT INTO tblSample VALUES ('A-1103', 'The LO 1014')
INSERT INTO tblSample VALUES ('A-1104', 'The AE 1014')
INSERT INTO tblSample VALUES ('A-1105', 'The PW 1014')
INSERT INTO tblSample VALUES ('A-1106', 'The QW 1014')
I'm currently inserting data from tblSample to tblPrimary with the following query:
INSERT INTO tblPrimary
SELECT s.SampleID FROM tblSample s
LEFT JOIN tblPrimary p on s.SampleId = p.SampleID
WHERE s.SampleId NOT IN (SELECT SampleID FROM tblPrimary)
Now I want to insert data into tblSecondary also, during the data insert into tblPrimary.
The newly generated `tblPrimary.PrimaryId` will be inserted into the tblSecondary.PrimiaryId` column
`tblSample.Name` will be inserted into the `tblSecondary.SampleName` column
It will be a cascading data inserting process
What do I need to do after the above insert query for this to get done?
I want the tblSecondary result to be as follows:
You will need a table variable and output clause for this something like....
DECLARE #NewIds (ID INT, SampleID varchar(8));
insert into tblPrimary(SampleID)
OUTPUT inserted.ID, inserted.SampleID INTO #NewIds (ID,SampleID )
select s.SampleID
from tblSample s
left join tblPrimary p on s.SampleId = p.SampleID
where s.SampleId not in (select SampleID from tblPrimary)
-- Now insert rows into tblSecondary table
INSERT INTO tblSecondary(PrimaryId, SampleName )
SELECT n.ID , S.Name
FROM tblSample s
INNER JOIN #NewIds n ON s.SampleId = n.SampleID

Inserting Multiple values of int type into one column

Hey so I am new to sql and am building an inventory management system. So for the database i am getting stuck at this point where I need to insert the various user id's to different Teams in the company, hence my problem arises when I am trying to assign multiple int values to a particular team.The DB is made in a manner where it requires a TeamId and the corresponding UserId's to go with it.
Possible this be helpful for you -
DECLARE #temp TABLE (ID INT)
-- For 2008 and higher
INSERT INTO #temp (ID)
VALUES (1), (2), (3)
-- For 2005 and higher
INSERT INTO #temp (ID)
SELECT ID
FROM (
SELECT ID = 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
) t
SELECT *
FROM #temp
Update (comment #Sivakumar: "1,19 is not an integer. It is a varchar."):
DECLARE #temp TABLE (txt varchar(500))
INSERT INTO #temp (txt)
VALUES ('1,19'), ('2,18')
SELECT t.c.value('.', 'INT')
FROM (
SELECT txml = CAST('<t>' + REPLACE(txt, ',', '</t><t>') + '</t>' AS XML)
FROM #temp
) a
CROSS APPLY txml.nodes('/t') AS t(c)
It sounds like a one-to-many relationship: a Team can have zero or more Users.
So create a foreign key column in User that references the Team primary key.
Something like this (check my syntax):
create table team (
id int not null auto_increment,
primary key(id);
);
create table user (
id int not null auto_increment,
tid int,
primary key(id),
foreign key tid references(team);
);
select *
from team t
join user u
on t.id = u.tid;

Insert from single table into multiple tables, invalid column name error

I am trying to do the following but getting an "Invalid Column Name {column}" error. Can someone please help me see the error of my ways? We recently split a transaction table into 2 tables, one containing the often updated report column names and the other containing the unchanging transactions. This leave me trying to change what was a simple insert into 1 table to a complex insert into 2 tables with unique columns. I attempted to do that like so:
INSERT INTO dbo.ReportColumns
(
FullName
,Type
,Classification
)
OUTPUT INSERTED.Date, INSERTED.Amount, INSERTED.Id INTO dbo.Transactions
SELECT
[Date]
,Amount
,FullName
,Type
,Classification
FROM {multiple tables}
The "INSERTED.Date, INSERTED.Amount" are the source of the errors, with or without the "INSERTED." in front.
-----------------UPDATE------------------
Aaron was correct and it was impossible to manage with an insert but I was able to vastly improve the functionality of the insert and add some other business rules with the Merge functionality. My final solution resembles the following:
DECLARE #TransactionsTemp TABLE
(
[Date] DATE NOT NULL,
Amount MONEY NOT NULL,
ReportColumnsId INT NOT NULL
)
MERGE INTO dbo.ReportColumns AS Trgt
USING ( SELECT
{FK}
,[Date]
,Amount
,FullName
,Type
,Classification
FROM {multiple tables}) AS Src
ON Src.{FK} = Trgt.{FK}
WHEN MATCHED THEN
UPDATE SET
Trgt.FullName = Src.FullName,
Trgt.Type= Src.Type,
Trgt.Classification = Src.Classification
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
FullName,
Type,
Classification
)
VALUES
(
Src.FullName,
Src.Type,
Src.Classification
)
OUTPUT Src.[Date], Src.Amount, INSERTED.Id INTO #TransactionsTemp;
MERGE INTO dbo.FinancialReport AS Trgt
USING (SELECT
[Date] ,
Amount ,
ReportColumnsId
FROM #TransactionsTemp) AS Src
ON Src.[Date] = Trgt.[Date] AND Src.ReportColumnsId = Trgt.ReportColumnsId
WHEN NOT MATCHED BY TARGET And Src.Amount <> 0 THEN
INSERT
(
[Date],
Amount,
ReportColumnsId
)
VALUES
(
Src.[Date],
Src.Amount,
Src.ReportColumnsId
)
WHEN MATCHED And Src.Amount <> 0 THEN
UPDATE SET Trgt.Amount = Src.Amount
WHEN MATCHED And Src.Amount = 0 THEN
DELETE;
Hope that helps someone else in the future. :)
Output clause will return values you are inserting into a table, you need multiple inserts, you can try something like following
declare #staging table (datecolumn date, amount decimal(18,2),
fullname varchar(50), type varchar(10),
Classification varchar(255));
INSERT INTO #staging
SELECT
[Date]
,Amount
,FullName
,Type
,Classification
FROM {multiple tables}
Declare #temp table (id int, fullname varchar(50), type varchar(10));
INSERT INTO dbo.ReportColumns
(
FullName
,Type
,Classification
)
OUTPUT INSERTED.id, INSERTED.fullname, INSERTED.type INTO #temp
SELECT
FullName
,Type
,Classification
FROM #stage
INSERT into dbo.transacrions (id, date, amount)
select t.id, s.datecolumn, s.amount from #temp t
inner join #stage s on t.fullname = s.fullname and t.type = s.type
I am fairly certain you will need to have two inserts (or create a view and use an instead of insert trigger). You can only use the OUTPUT clause to send variables or actual inserted values ti another table. You can't use it to split up a select into two destination tables during an insert.
If you provide more information (like how the table has been split up and how the rows are related) we can probably provide a more specific answer.

Select row data as ColumnName and Value

I have a history table and I need to select the values from this table in ColumnName, ColumnValue form. I am using SQL Server 2008 and I wasn’t sure if I could use the PIVOT function to accomplish this. Below is a simplified example of what I need to accomplish:
This is what I have:
The table’s schema is
CREATE TABLE TABLE1 (ID INT PRIMARY KEY, NAME VARCHAR(50))
The “history” table’s schema is
CREATE TABLE TABLE1_HISTORY(
ID INT,
NAME VARCHAR(50),
TYPE VARCHAR(50),
TRANSACTION_ID VARCHAR(50))
Here is the data from TABLE1_HISTORY
ID NAME TYPE TRANSACTION_ID
1 Joe INSERT a
1 Bill UPDATE b
1 Bill DELETE c
I need to extract the data from TABLE1_HISTORY into this format:
TransactionId Type ColumnName ColumnValue
a INSERT ID 1
a INSERT NAME Joe
b UPDATE ID 1
b UPDATE NAME Bill
c DELETE ID 1
c DELETE NAME Bill
Other than upgrading to Enterprise Edition and leveraging the built in change tracking functionality, what is your suggestion for accomplishing this task?
You could try using a UNION
Test Data
DECLARE #TABLE1_HISTORY TABLE (
ID INT,
NAME VARCHAR(50),
TYPE VARCHAR(50),
TRANSACTION_ID VARCHAR(50))
INSERT INTO #TABLE1_HISTORY
SELECT 1, 'Joe', 'INSERT', 'a'
UNION ALL SELECT 1, 'Bill', 'UPDATE', 'b'
UNION ALL SELECT 1, 'Bill', 'DELETE', 'c'
SQL Statement
SELECT [TransactionID] = Transaction_ID
, [Type] = [Type]
, [ColumnName] = 'ID'
, [ColumnValue] = CAST(ID AS VARCHAR(50))
FROM #Table1_History
UNION ALL
SELECT [TransactionID] = Transaction_ID
, [Type] = [Type]
, [ColumnName] = 'NAME'
, [ColumnValue] = [Name]
FROM #Table1_History
ORDER BY TransactionID
, ColumnName
This can be done with the UNPIVOT function in SQL Server:
select transaction_id,
type,
ColumnName,
ColumnValue
from
(
select transaction_id,
type,
cast(id as varchar(50)) id,
name
from TABLE1_HISTORY
) src
unpivot
(
ColumnValue
for ColumnName in (ID, Name)
) un

Resources