Not able to parse XML - sql-server

I need help parsing an XML variable into a table format as below. I am new to parsing looked up in google and was able to parse AccountId, Email, ProductName and ProductType but not able to parse nodes with in attribute like CustName, CreatedDate, billingCode
DECLARE #xmlvalue XML =
'<Request>
<AccountId>16000</AccountId>
<Email>test#gmail.com</Email>
<Attributes>
<Attribute><Name xmlns="http://testtarget.com/wsdl/myAPI">CustName</Name><Value xmlns="http://testtarget.com/wsdl/myAPI">TestName</Value></Attribute>
<Attribute><Name xmlns="http://testtarget.com/wsdl/myAPI">CreatedDate</Name><Value xmlns="http://testtarget.com/wsdl/myAPI">3/26/2022</Value></Attribute>
<Attribute><Name xmlns="http://testtarget.com/wsdl/myAPI">BillingCode</Name><Value xmlns="http://testtarget.com/wsdl/myAPI">testbiling</Value></Attribute>
</Attributes>
<ProductName>TestProduct</ProductName>
<ProductType>Recurring</ProductType>
</Request>'
Expected OUTPUT :
AccountId Email CustName CreatedDate BillingCode ProductName ProductType
1600 test#gmail.com TestName 03/26/2022 testbiling TestProduct Recurring
Below code is giving me what I wanted. Is there an easy approach?
SELECT req.req_col.value('AccountId[1]','INT') AS AccountId,
req.req_col.value('Email[1]','VARCHAR(100)') AS Email,
req.req_col.value('ProductName[1]','VARCHAR(100)') AS ProductName,
req.req_col.value('ProductType[1]','VARCHAR(100)') AS ProductType,
v.CustName,
v.CreatedDate,
v.BillingCode
FROM #xmlvalue.nodes('/Request') req(req_col)
CROSS APPLY ( SELECT *
FROM (SELECT req.req_col.value('Name[1]','VARCHAR(100)') AS Name,
req.req_col.value('Value[1]','VARCHAR(100)') AS Value
FROM #xmlvalue.nodes('/Request/Attributes/Attribute') req(req_col)
) tab1
PIVOT (
MAX(Value) FOR Name IN ( CustName, CreatedDate, BillingCode )) Tab2
) v

It is not necessary to use PIVOT for this, you can use filters in XPath expressions to select the various Attribute Name-Value element pairs, e.g.:
with xmlnamespaces (
'http://testtarget.com/wsdl/myAPI' as anything
)
select
request.value(N'(AccountId/text())[1]', N'nvarchar(50)') as AccountId,
request.value(N'(Email/text())[1]', N'nvarchar(50)') as Email,
request.value(N'(Attributes/Attribute[anything:Name/text()="CustName"]/anything:Value/text())[1]', N'nvarchar(50)') as CustName,
request.value(N'(Attributes/Attribute[anything:Name/text()="CreatedDate"]/anything:Value/text())[1]', N'nvarchar(50)') as CreatedDate,
request.value(N'(Attributes/Attribute[anything:Name/text()="BillingCode"]/anything:Value/text())[1]', N'nvarchar(50)') as BillingCode,
request.value(N'(ProductName/text())[1]', N'nvarchar(50)') as ProductName,
request.value(N'(ProductType/text())[1]', N'nvarchar(50)') as ProductType
from #xmlvalue.nodes('/Request') foo(request);
AccountId
Email
CustName
CreatedDate
BillingCode
ProductName
ProductType
16000
test#gmail.com
TestName
3/26/2022
testbiling
TestProduct
Recurring

Related

Question: How do I write the command to find the customer who placed an order number 12345?

Sales database:
Customer (custId, lastName, firstName, address, phone, creditLimit)
Order (orderNumber, date, total, custID)
LineItem (orderNumber, itemNumber, qtyOrdered).
Item (itemNumber, itemName, price)
For the Sales database shown above, write the SQL command to find the name and address of the customer who placed order number 12345.
Try this:
select
firstName + ' ' + lastName [Customer]
, [address]
, [phone]
from [Order] o
inner join [Customer] c on c.custId = o.custId
where o.orderNumber = '12345'
Let me know if it worked.

How to use DISTINCT keyword in SQL Server?

How to use DISTINCT keyword in SQL Server? I mean if it can work for given field.
select id, name, age
from dbo.XXX
There are multiple row returned by the query. I would like to get how many kinds of id or name or age.
select **distinct** id, name, age from dbo.XXX or
select id, **distinct** name, age from dbo.XXX or
select id, name, **distinct** age from dbo.XXX
To sum up, I would like to use a single SQL to get the distinct count of each fields, like select π—±π—Άπ˜€π˜π—Άπ—»π—°π˜ id, π—±π—Άπ˜€π˜π—Άπ—»π—°π˜ name, π—±π—Άπ˜€π˜π—Άπ—»π—°π˜ age from dbo.XXX
Dense_Rank can be used to calculate a distinct count for any column and multiple columns:
Select col1, col2, col3,
dense_rank() over (partition by [col1] order by [Unique ID]) + dense_rank() over (partition by [col1] order by [Unique ID] desc) - 1 as DistCountCol1,
dense_rank() over (partition by [col2] order by [Unique ID]) + dense_rank() over (partition by [col2] order by [Unique ID] desc) - 1 as DistCountCol2,
dense_rank() over (partition by [col3] order by [Unique ID]) + dense_rank() over (partition by [col3] order by [Unique ID] desc) - 1 as DistCountCol3
from [table]
select distinct ID
from dbo.XXX
Select distinct name
from dbo.XXX
Select distinct age
from dbo.XXX
If you want to know how many rows you have for each distinct ID or Name or Age, you can use the following:
Select ID, count(id) as [ID_Recurrence]
from dbo.XXX
group by ID
Select Age, count(age) as [Age_Recurrence]
from dbo.XXX
group by Age
Select Name, count(name) as [Name_Recurrence]
from dbo.XXX
group by Name
The DISTINCT keyword return a unique row like the Following
SELECT DISTINCT ID FROM SomeTable
SELECT DISTINCT ID , SCORE FROM SomeTable
If you want to get unique value from row try the following code.
The Below code is copied from here
select t.id, t.player_name, t.team
from tablename t
join (select team, min(id) as minid from tablename group by team) x
on x.team = t.team and x.minid = t.id
select COUNT(distinct id) uniqueIDCount
from dbo.XXX
would count distinct values of id field, if you want to count distinct values for field combination you must concat fields, assuming your id is integer and name is nvarchar:
select COUNT(distinct CONVERT(nvarchar, id) + name) uniqueIDCount
from dbo.XXX
note that even this way looks nice it is probably not the most efficient one, here you have more efficient, but also more complicated method way:
with c as (
select distinct id, name
from dbo.XXX
)select COUNT(1)
from c
Not sure why it's complicated. U can have 3 different queries and u can union to return single set if u want .

Finding a recent most duplicate records from SQL Server 2012

I want to find the recent duplicate records from SQL Server 2012. Here is the table structure I have.
I have table name called UserRegistration which contains the duplicate of UserID(GUID) and in same table, I have CreatedDate Column as well (Date). Now I want to find the recent duplicate records from this table.
Here is the same data.
id FirstName LastName CreatedDate UserID
109 FirstNameA LastNameA 28-04-2015 GUID1
110 FirstNameC LastNameD 19-05-2015 GUID2
111 FirstNameE LastNameF 22-05-2015 GUID1
If you notice on above tables, GUID 1 are having the duplicate, Now I want to find the recent one means it should return me only those rows with duplication but recent data. So in above table structure, it should return me 111 because record has been created recently compared to the 109. I believe you understand.
Do let me know if you have any question. I am happy to answer. Thanks. Awaiting for the reply.
Harshal
Try the below query this should do the work based on your i/p data -
create table #UserRegistration (id int,FirstName varchar(20),LastName varchar(20),CreatedDate date,UserID varchar(20))
insert into #UserRegistration
select 109, 'FirstNameA', 'LastNameA', '2015-04-28', 'GUID1' union
select 110, 'FirstNameC', 'LastNameD', '2015-05-19', 'GUID2' union
select 111, 'FirstNameE', 'LastNameF', '2015-05-22', 'GUID1'
select id, FirstName, LastName, CreatedDate, UserID from
(SELECT ur.*,row_number() over(partition by UserID order by CreatedDate) rn
FROM #UserRegistration ur) A
where rn > 1
You could use CTE. Group your records by UserID and give your particular row a rank ordered by CreatedDate.
insert into tab(id, FirstName, LastName, CreatedDate, UserID)
values(109, 'FirstNameA', 'LastNameA', '2015-04-28', 'guid1'),
(110, 'FirstNameC', 'LastNameD', '2015-05-19', 'guid2'),
(111, 'FirstNameE', 'LastNameF', '2015-05-22', 'guid1');
with cte as
(
select id, ROW_NUMBER() over (partition by UserID order by CreatedDate asc) as [Rank],
FirstName, LastName, CreatedDate, UserID
from tab
)
select id, FirstName, LastName, CreatedDate, UserID from cte where Rank > 1
Rank > 1 condition is responsible for retrieving duplicated items.
sqlfiddle link:
http://sqlfiddle.com/#!6/4d1f2/6
Solved this by using tmp-tables:
SELECT a.UserID,
MAX(a.CreatedDate) As CreatedDate
INTO #latest
FROM <your table> a
GROUP BY a.UserID
HAVING COUNT(a.UserID) > 1
SELECT b.id
FROM #latest a
INNER JOIN <your table> b ON a.UserID = b.UserID AND a.CreatedDate = b.CreatedDate
try this,
SELECT * FROM TableName tt WHERE
exists(select MAX(createdDate)
from TableName
where tt.UserID = UserID
group by UserID
having MAX(createdDate)= tt.createdDate)
I think your createddate field is not a date field, then try Format
WITH TempAns (id,UserID,duplicateRecordCount)
AS
(
SELECT id,
UserID,
ROW_NUMBER()OVER(partition by UserID ORDER BY id)
AS duplicateRecordCount
FROM #t
)
select * from #t where id in (
select max(id )
from TempAns
where duplicateRecordCount > 1
group by name )
You'd rank your records with ROW_NUMBER() to give all last records per userid #1. With COUNT() you make sure only to get the userids having more than one record.
select
id, firstname, lastname, createddate, userid
from
(
select
id, firstname, lastname, createddate, userid,
row_number() over (partition by userid oder by createddate desc) as rn,
count(*) over (partition by userid) as cnt
from userregistration
) ranked
where rn = 1 -- only last one
and cnt > 1; -- but only if there is more than one record for the userid
This gets the latest record for every userid that has duplicates.

TSQL Union, Max(date) - have solution. Want improvement

The following query works, but there has to be a better way to set the value of a table to the max date of the union of two sets of data. Here's what I have:
Update stagingTable
Set OverrideFlag =
(
select total.overrideflag from
(
select Customer_SSN as ssn, RequestDateTime as maxdate, overrideflag
from tableA
where RequestDateTime > '9/1/2012'
union
select ssn, EntryDate as maxdate, overrideflag
from tableB
where EntryDate > '9/1/2012'
) total
join
(
select ssn, max(maxdate) as maxdate from
(
select Customer_SSN as ssn, RequestDateTime as maxdate
from tableA
where RequestDateTime > '9/1/2012'
union
select ssn, EntryDate as maxdate
from tableB
where EntryDate > '9/1/2012'
) maxs
group by ssn
) maxdates on total.ssn = maxdates.ssn and total.maxdate = maxdates.maxdate where total.ssn = stagingTable.ssn
)
It appears you are doing the exact same thing twice, so I am not getting the need to define something twice and join it back to itself unless there was something different in one of the nested selects. You are essentially writing the same statement twice and the redundancy may be an issue as one of the selects appears completely redundant.
-- this is a CTE and is better for reuse than a nested select as you can reference it
-- is as a base and reuse that, versus having to write the same statement twice.
;with a as
(
select
Customer_SSN as ssn,
RequestDateTime as maxdate,
OverRideFlag,
-- EDIT, you should be able to use a 'Windowed function' to get the maxDate
max(RequestDateTime) over(partition by SSN order by RequestDateTime desc) as maxDate
from tableA
where RequestDateTime > '9/1/2012'
union
select
ssn,
EntryDate,
OverRideFlag,
max(RequestDateTime) over(partition by SSN order by RequestDateTime desc) as maxDate
from tableB
where EntryDate > '9/1/2012'
)
Update stagingTable
Set OverrideFlag = total.overrideflag
from a total
join stagingTable on total.ssn = stagingTable.ssn
-- do not know reference here so you need to add that table as a 'join'
where total.EntryDate = total.maxDate
I also found a different way to do it with a temp table. I'm becoming very comfortable with these, but I always want to see a different way how to do this. Not disappointed!
create table #tOvr(customerID varchar(15), ssn varchar(11), EntryDate datetime, overrideflag varchar(2))
insert into #tOvr
select customer_ID, Customer_SSN, RequestDateTime, overrideflag
from tableA
where RequestDateTime > '9/1/2012'
and Customer_ID in
(select contact_ID from stagingTable
where Location_ID = #Location_ID)
insert into #tOvr
select Customer_ID, ssn, EntryDate, overrideflag
from tableB
where EntryDate > '9/1/2012'
and Customer_ID in
(select contact_ID from stagingTable
where Location_ID = #Location_ID)
Update stagingTable
Set OverrideFlag =
(select overrideflag from #tOvr
where EntryDate = (select max(EntryDate) from #tOvr where #tOvr.customerID = stagingTable.contact_ID)
)

SQL Select distinct from multiple fields returning only one row

I have a table with the following columns in SQL Server:
MEMBERID, MEMBEREMAIL, FATHEREMAIL, MOTHEREMAIL, MEMBERNAME
MEMBERID is PK. The three email columns are not unique, so the same email may appear several times in the same row AND in several rows.
I am trying to extract a unique list of emails, and for each email also get a memberid and membername (it does not matter from which record).
For example if I have three rows:
1 x#x.com y#y.com y#y.com Mark
2 z#z.com y#y.com x#x.com John
3 x#x.com y#y.com z#z.com Susan
I want to get the three emails (x#x.com, y#y.com, z#z.com) and for each of those a MEMBERID in which they appear. It does NOT which MEMBERID (for example for x#X.com I don't care if I get the values 1 and Mark or 2 and John or 3 and Susan, as long as x#x.com appears only once in the results.
If I use DISTINCT when trying to return the email and memberid and membername, of course I get all of the rows.
You could use a subquery to normalize all emails. Then you can use row_number to filter out one memberid, membername per email:
select *
from (
select row_number() over (partition by email order by memberid) as rn
, *
from (
select MEMBERID
, MEMBERNAME
, MEMBEREMAIL as email
from YourTable
union all
select MEMBERID
, MEMBERNAME
, FATHEREMAIL
from YourTable
union all
select MEMBERID
, MEMBERNAME
, MOTHEREMAIL
from YourTable
) as emails
) num_emails
where rn = 1
You could also normalize the emails using the UNPIVOT clause, like this:
select *
from (
select row_number() over (partition by email order by memberid) as rn
, *
from (
select MEMBERID
, MEMBERNAME
, email
from YourTable
unpivot (
email
for emailOwner
in (
MEMBEREMAIL,
FATHEREMAIL,
MOTHEREMAIL
)
) as u
) as emails
) num_emails
where rn = 1
Try both versions at SQL Fiddle:
UNION ALL version
UNPIVOT version
This code will give you the right group of distinct emails:
then you can create a cursor out of the query members and then get the comma seperated list of mails per memberid with this concept I would create an output table for this will be easyer if you need it for future use and would make a store procedure for this to create the custom table
select mem.*, mails.MEMBEREMAIL
from (
select MEMBERID,max(MEMBERNAME) as MEMBERNAME
from table
group by MEMBERID
) as mem
inner join
(
select distinct MEMBERID, MEMBEREMAIL
from (
select MEMBERID, MEMBEREMAIL
from table
union
select MEMBERID, FATHEREMAIL
from table
union
select MEMBERID, MOTHEREMAIL
from table
) as mail
) as mails on mem.MEMBERID = mails.MEMBERID

Resources