sample Input Table Details:
Expected Output:
<Geo>
<Continent Name="Asia">
<Country Name="China">
<State Name="Bejing"></State>
</Country>
<Country Name="Japan">
<State Name="Tokyo"></State>
</Country>
</Continent>
<Continent Name="Europe">
<Country Name="Germany">
<State Name="Berlin"></State>
</Country>
<Country Name="France">
<State Name="Paris"></State>
</Country>
</Continent>
</Geo>
How to convert Input relation Data as Above Output XML?
Something like this:
create table #continents (
continentid int, [continent name] nvarchar(100))
insert into #continents
SELECT 1, 'Asia'
UNION ALl
SELECT 2, 'Europe'
UNION ALL
SELECT 3, 'Africa'
CREATE TABLE #countries (countryid int, [country name] nvarchar(100), continentid INT)
insert into #countries
SELECT 1,'China', 1
UNION ALL
SELECT 2, 'Japan', 1
UNION ALL
SELECT 3, 'Germany', 2
UNION ALL
SELECT 4, 'France', 2
CREATE TABLE #states (stateId INT, [State Name] nvarchar(100), countryid INT)
INSERT INTO #states
SELECT 1, 'Tokya', 2
UNION ALL
SELECT 2, 'Berlin', 3
UNION ALL
SELECT 3, 'Paris', 4
UNION ALL
SELECT 4, 'Bejing', 1
select c.[Continent Name] AS [#Name]
, (
select cc.[Country Name] AS [#Name]
, (
SELECT [State Name] AS [#Name]
FROM #states ss
WHERE ss.countryId = cc.countryId
FOR XML PATH('State'), TYPE
)
FROM #countries cc
WHERE cc.continentId = c.continentId
FOR XML PATH('Country'), TYPE
)
from #continents c
for xml Path('Continent'), ROOT('Geo'), TYPE
Btw, next time, try to post data as create / insert script, instead of a screenshot, it really saves a lot of typing
I was just too late copying all the data....
WITH Continent AS (
SELECT 1 as Continent_ID, 'Asia' as Continent_Name
UNION ALL
SELECT 2, 'Europe'
UNION ALL
SELECT 3,'Africa'),
Country AS (
SELECT 1 as Country_ID, 'China' as Country_Name, 1 as Continent_ID
UNION ALL
SELECT 2,'Japan', 1
UNION ALL
SELECT 3, 'Germany', 2),
State AS (
SELECT 1 as State_ID, 'Tokya' as State_Name, 2 as Country_ID
UNION ALL
SELECT 2, 'Berlin', 3 )
select Continent_Name, Country_Name, State_Name
from Continent
inner join Country on Country.Continent_ID = Continent.Continent_ID
inner join State ON State.Country_ID = Country.Country_ID FOR XML AUTO, Root('Geo'), TYPE;
output (after formatting):
<Geo>
<Continent Continent_Name="Asia">
<Country Country_Name="Japan">
<State State_Name="Tokya"/>
</Country>
</Continent>
<Continent Continent_Name="Europe">
<Country Country_Name="Germany">
<State State_Name="Berlin"/>
</Country>
</Continent>
</Geo>
More info: FOR XML (SQL Server)
A SELECT query returns results as a rowset. You can optionally
retrieve formal results of a SQL query as XML by specifying the FOR
XML clause in the query. The FOR XML clause can be used in top-level
queries and in subqueries. The top-level FOR XML clause can be used
only in the SELECT statement. In subqueries, FOR XML can be used in
the INSERT, UPDATE, and DELETE statements. FOR XML can also be used in
assignment statements.
Related
I've a table source
idGeo GEO PARENTID
1 EMEA NULL
2 France 1
3 mIDCAPSfRANCE 2
4 Germany 1
5 France exl midcaps 2
6 Amercias NULL
7 US 6
The expected result of the hierarchy
I tried to do left join(self join) but I'm not able to get exactly as expected.
Here is a generic method regardless of the level of the hierarchy.
SQL
-- DDL and sample data population, start
DECLARE #tbl table (
idGeo INT IDENTITY PRIMARY KEY,
GEO VARCHAR(64),
PARENTID INT
);
insert into #tbl (GEO, PARENTID) values
( 'EMEA', NULL),
( 'France', 1),
( 'mIDCAPSfRANCE', 2),
( 'Germany', 1),
( 'France exl midcaps', 2),
( 'Amercias', NULL),
( 'US', 6);
-- DDL and sample data population, end
--SELECT * FROM #tbl;
WITH cte AS
(
-- Anchor query
SELECT idGEO, GEO, ParentID, 1 AS [Level]
, CAST('/' + GEO AS VARCHAR(1000)) AS XPath
FROM #tbl
WHERE ParentID IS NULL
UNION ALL
-- Recursive query
SELECT t.idGEO, t.GEO, t.ParentID, cte.[Level] + 1 AS [Level]
, CAST(cte.[XPath] + '/' + t.GEO AS VARCHAR(1000)) AS [XPath]
FROM #tbl AS t
INNER JOIN cte ON t.ParentID = cte.idGEO
WHERE t.ParentID IS NOT NULL
)
SELECT idGEO
, REPLICATE(' ',[Level]-1) + GEO AS GEOHierarchy
, GEO, ParentID, [Level], [XPath]
FROM cte
ORDER BY XPath;
Output
So if I understand you want to generate columns as the levels go on.
You cannot dynalically generate columns, SQL is a fixed column language.
I am using SQL Server and want to do Bulk Updates but don't want any row to be re-updated if same values already exists.
For Example Consider following tables with their data
DECLARE #UpdatedIds TABLE
(
BookId INT
)
CREATE TABLE Book
(
[Id] INT,
[Name] NVARCHAR(MAX)
)
INSERT INTO Book([Id], [Name])
SELECT 1, 'Book1'
UNION
SELECT 2, 'Book2'
UNION
SELECT 3, 'Book3'
UNION
SELECT 4, 'Book4'
UNION
SELECT 5, 'Book5'
CREATE TABLE #Book
(
[Id] INT,
[Name] NVARCHAR(MAX)
)
INSERT INTO #Book([Id], [Name])
SELECT 1, 'Book1_Changed'
UNION
SELECT 2, 'Book2_Changed'
UNION
SELECT 3, 'Book3'
UNION
SELECT 4, 'Book4'
UNION
SELECT 5, 'Book5'
What I want is when I run the following query
UPDATE [BO]
SET [Name] = [BU].[Name]
OUTPUT [INSERTED].[Id] INTO #UpdatedIds([BookId])
FROM [Book] [BO]
INNER JOIN #Book [BU] ON [BO].[Id] = [BU].[Id]
SELECT * FROM #UpdatedIds
#UpdatedIds should have records of 1 and 2 not 3, 4, or 5
I have 2 ways of doing this in my mind and would like to have opinions about which one will be fastest/better.
Using Except Keyword and delete rows that already exists
Using Binary_Checksum that is comparing Checksums of records in actual table and in temp table and removing records with equal checksum
Thanks
I have a table with 50-60 columns having master data,something like below (just example).
I need to show filters to allow the customer to filter the data. So the result would be something like below, which I will bind to drop-downs on web page.
So, I though of having a FilterOptions table which tells me which column to look for which drop-down filter, something like:
Now, I am not able to figure out sql query I need to get data out in format as in second image. Probably need to pivot MasterData table and join with FilterOptions, but how, not sure.
Any help/pointer is appreciated.
Thanks,
I would suggest using apply:
select v.*
from t cross apply
(values ('Country', t.countryCode, t.countryName),
('Country', t.StateCode, t.StatueName),
('City', t.CityCode, t.CityName),
) v(label, key, value);
You can use unpivot like below:
declare #master table(id int, countrycode varchar(10), countryname varchar(20), statecode varchar(10), statename varchar(20), citycode varchar(10), cityname varchar(20))
insert into #master
select 1, 'IN', 'INDIA', 'MH', 'Maharashtra', 'PNE', 'Pune'
union
select 2, 'US', 'USA', 'PA', 'Pennsylvania', 'PH', 'Philadelphia'
union
select 3, 'US', 'USA', 'NY', 'NewYork', 'NY', 'NewYork'
union
select 4, 'US', 'USA', 'NY', 'Pennsylvania', 'PT', 'NewYork'
declare #filters table (label varchar(20), [key] varchar(10), [value] varchar(20))
insert into #filters
select 'Country', 'IN', 'India'
union
select 'Country', 'US', 'USA'
union
select 'State', 'MH', 'MH'
union
select 'State', 'PA', 'Pennsylvania'
union
select 'City', 'NY', 'NewYork'
union
select 'City', 'PNE', 'Pune'
select id,cc countrycode, ccval, cn countryname, cnval, sc statecode, scval, sn statename, snval, cic citycode, cival, cin cityname, cinval
into #temp
from
(
select id, countrycode, countryname, statecode, statename, citycode, cityname
from #master
)t
unpivot
(
cc for ccval in (countrycode)
)upvtcountry
unpivot
(
cn for cnval in (countryname)
)upvtcountryname
unpivot
(
sc for scval in (statecode)
)upvtstate
unpivot
(
sn for snval in (statename)
)upvtstatename
unpivot
(
cic for cival in (citycode)
)upvtcity
unpivot
(
cin for cinval in (cityname)
)upvtcityname
select distinct f.label, case f.label when 'City' then t.cival when 'Country' then t.ccval when 'State' then t.scval end keycolumnname
,case f.label when 'City' then t.cinval when 'Country' then t.cnval when 'State' then t.snval end valucolumnname
from #temp t
left join #filters f
on f.[key] = case f.label when 'City' then t.citycode when 'Country' then t.countrycode when 'State' then t.statecode end
drop table #temp
I have an account table and a campaign table, each account has certain number of campaigns associated with it. No I want to export the account IDs and all the Campaign IDs associated with that account id in to XML in this structure
<Accounts>
<Account>
<AccountID></AccountID>
<AccountName></AccountName>
<CampaignIDs>
<CampaignID></CampaignID>
<CampaignID></CampaignID>
</CampaignIDs>
</Account>
</Accounts>
I am using XML Explicit to control the output of the data into XML and here is what I have got so far.
SELECT
1 AS Tag,
NULL AS Parent,
NULL AS 'Accounts!1',
NULL AS 'Account!2!AccountID!Element',
NULL AS 'Account!2!AccountName!Element',
NULL AS 'Account!2!FMID!Element'
UNION ALL
SELECT
2 AS Tag,
1 AS Parent,
NULL,
a.id as AccountID,
a.Name as AccountName,
NULL
from Account a
FOR XML EXPLICIT
Now I want to execute another query like Select id from campaign where accountid = var
and then append all those campaign IDs to the xml structure.
How do I go about this?
I would recommend using FOR XML PATH instead of FOR XML EXPLICIT - it's much easier to use and much more expressive.
See this:
-- set up test data
declare #Accounts table (AccountID INT, AccountName VARCHAR(50))
declare #Campaigns table (CampaignID INT, AccountID INT, CampaignName varchar(50))
insert into #Accounts values(1, 'Account #1'),(2, 'Account #2')
insert into #Campaigns values(1, 1, 'Campaign #1-1'), (2, 1, 'Campaign #2-1'), (3, 1, 'Campaign #3-1'),
(4, 2, 'Campaign #1-2'), (5, 2, 'Campaign #2-2')
-- SELECT with FOR XML PATH and a nested SELECT/FOR XML PATH,TYPE
select
AccountID,
AccountName,
(SELECT CampaignID
FROM #Campaigns c
WHERE c.AccountID = a.AccountID
FOR XML PATH(''),TYPE) AS 'CampaignIDs'
FROM
#Accounts a
FOR XML PATH('Account'),ROOT('Accounts')
This SELECT statement gives me the following output:
<Accounts>
<Account>
<AccountID>1</AccountID>
<AccountName>Account #1</AccountName>
<CampaignIDs>
<CampaignID>1</CampaignID>
<CampaignID>2</CampaignID>
<CampaignID>3</CampaignID>
</CampaignIDs>
</Account>
<Account>
<AccountID>2</AccountID>
<AccountName>Account #2</AccountName>
<CampaignIDs>
<CampaignID>4</CampaignID>
<CampaignID>5</CampaignID>
</CampaignIDs>
</Account>
</Accounts>
I'm new-ish to SQL and am trying to figure out how to use the values from the Select statement in a While Exists conditional loop. The purpose is to combine multiple occurences of an attribute for a Document into a single field, and later pivot and join those results to the Document record.
For example, three tables exist like so:
ATTRIBUTES TABLE
ID, ATTRIBUTE_NAME
---------------------------
1, Created
2, Embedded_Image
...
ATTRIBUTE_VALUES TABLE
ATTRIBUTE_ID, VALUE, DOC_ID
-------------------------------------------
1, 2010/11/01, 1
2, 'Home.png', 1
2, 'Castle.png', 1
2, 'Apartment.jpg', 1
1, 2008/06/23, 2
2, 'Ski Jump.jpg', 2
2, 'Snowboarding.png', 2
...
DOCUMENTS TABLE
ID, TEXT
---------------------------
1, 'Homes of the ...'
2, 'Winter sports ...'
...
So a final Pivot and Join of the tables would look like so:
DOC_ID, TEXT, Created, Embedded_Image
----------------------------------------------------------------------------------------
1, 'Homes of the ...', 2010/11/01, 'Home.png,Castle.png,Apartment.jpg'
2, 'Winter sports ...', 2008/06/23, 'Ski Jump.jpg, Snowboarding.png'
The SQL While Exists condition I've tried to write looks like so:
DECLARE #LOOP_DOC_ID UNIQUEIDENTIFIER
DECLARE #LOOP_ATTRIBUTE_NAME NVARCHAR(MAX)
WHILE EXISTS(
SELECT [dbo].[ATTRIBUTE_VALUES].[DOC_ID], [dbo].[ATTRIBUTES].[ATTRIBUTE_NAME]
FROM ([dbo].[ATTRIBUTE_VALUES] INNER JOIN [dbo].[ATTRIBUTES]
ON [dbo].[ATTRIBUTE_VALUES].[ATTRIBUTE_ID] = [dbo].[ATTRIBUTES].[ID])
)
BEGIN
SET #LOOP_DOC_ID = DOC_ID
SET #LOOP_ATTRIBUTE_NAME = ATTRIBUTE_NAME
SELECT STUFF(
(
SELECT DISTINCT ',' + RTRIM(LTRIM([dbo].[ATTRIBUTE_VALUES].[VALUE]))
FROM
(
[dbo].[ATTRIBUTE_VALUES] INNER JOIN [dbo].[ATTRIBUTES]
ON [dbo].[ATTRIBUTE_VALUES].[ATTRIBUTE_ID] = [dbo].[ATTRIBUTES].[ID]
)
WHERE [dbo].[ATTRIBUTE_VALUES].[DOC_ID] = #LOOP_DOC_ID
AND [dbo].[ATTRIBUTES].[ATTRIBUTE_NAME] = #LOOP_ATTRIBUTE_NAME
ORDER BY ',' + RTRIM(LTRIM([dbo].[ATTRIBUTE_VALUES].[VALUE]))
FOR XML PATH('')
), 1, 2, ''
) AS VALUE, #LOOP_DOC_ID AS DOC_ID, #LOOP_ATTRIBUTE_NAME AS ATTRIBUTE_NAME
END
SQL Server doesn't like the lines where I'm trying to SET the variables to the values from the Select statement in the While Exists condition.
How can I use the [dbo].[ATTRIBUTE_VALUES].[DOC_ID], [dbo].[ATTRIBUTES].[ATTRIBUTE_NAME] values Selected in the While Exists conditional statement between the BEGIN and END statements?
Preferrably I would like to do away with the #LOOP_DOC_ID and #LOOP_ATTRIBUTE_NAME variables and deal directly with the values.
I've looked through forums that have talked about using Cursors to solve similar problems, but each one of them seem to recommend only using Cursors as a last resort due to their lack of speed. I've also seen some people use stored procedures, but I can't use those, since my boss has ruled those as off-limits. Am I in need of a Cursor, or is there a better way to do this?
Have a look at something like this (Full Example)
DECLARE #ATTRIBUTES TABLE(
ID INT,
ATTRIBUTE_NAME VARCHAR(100)
)
INSERT INTO #ATTRIBUTES SELECT 1, 'Created'
INSERT INTO #ATTRIBUTES SELECT 2, 'Embedded_Image'
DECLARE #ATTRIBUTE_VALUES TABLE(
ATTRIBUTE_ID INT,
VALUE VARCHAR(100),
DOC_ID INT
)
INSERT INTO #ATTRIBUTE_VALUES SELECT 1, '2010/11/01', 1
INSERT INTO #ATTRIBUTE_VALUES SELECT 2, 'Home.png', 1
INSERT INTO #ATTRIBUTE_VALUES SELECT 2, 'Castle.png', 1
INSERT INTO #ATTRIBUTE_VALUES SELECT 2, 'Apartment.jpg', 1
INSERT INTO #ATTRIBUTE_VALUES SELECT 1, '2008/06/23', 2
INSERT INTO #ATTRIBUTE_VALUES SELECT 2, 'Ski Jump.jpg', 2
INSERT INTO #ATTRIBUTE_VALUES SELECT 2, 'Snowboarding.png', 2
DECLARE #DOCUMENTS TABLE(
ID INT,
[TEXT] VARCHAR(100)
)
INSERT INTO #DOCUMENTS SELECT 1, 'Homes of the ...'
INSERT INTO #DOCUMENTS SELECT 2, 'Winter sports ...'
;WITH Vals AS (
SELECT d.ID DOC_ID,
d.[TEXT] [TEXT],
a.ATTRIBUTE_NAME,
av.VALUE
FROM #DOCUMENTS d INNER JOIN
#ATTRIBUTE_VALUES av ON d.ID = av.DOC_ID INNER JOIN
#ATTRIBUTES a ON av.ATTRIBUTE_ID = a.ID
)
SELECT *
FROM (
SELECT DOC_ID,
[TEXT],
ATTRIBUTE_NAME,
stuff(
(
select ',' + t.VALUE
from Vals t
where t.DOC_ID = v.DOC_ID
AND t.ATTRIBUTE_NAME = v.ATTRIBUTE_NAME
order by t.VALUE
for xml path('')
),1,1,'') Concats
FROM Vals v
GROUP BY DOC_ID,
[TEXT],
ATTRIBUTE_NAME
) s
PIVOT ( MAX(ConCats) FOR ATTRIBUTE_NAME IN ([Created],[Embedded_Image])) pvt
Output
DOC_ID TEXT Created Embedded_Image
1 Homes of the ... 2010/11/01 Apartment.jpg,Castle.png,Home.png
2 Winter sports ... 2008/06/23 Ski Jump.jpg,Snowboarding.png
From your sample, and with support from common sense, I venture the hypothesis that
A document has a single creation date.
A document can have many embedded images.
So pivoting on creation date is straightforward:
SELECT DOC_ID
, VALUE AS Created
FROM ATTRIBUTE_VALUES
WHERE ATTRIBUTE_ID = 1
and joining this subquery to your Documents table gives you the first three columns of your desired output.
Your final column summarizes multiple embedded images for each document. I personally would use some standard reporting tool (e.g. MS Access or Crystal Reports). Alternatively, create a new empty table with your four desired columns, populate the first three columns with a SQL INSERT statement, and then have Perl (or C#, or your favorite declarative language) query for the embedded images of each document, concatenate the results with commas, and insert the concatenation into your fourth column.
But if you want to do it in SQL, the concatenate-multiple-values question has been asked here before, e.g. in How to create a SQL Server function to "join" multiple rows from a subquery into a single delimited field?.