SQL server: WHERE in xml field - sql-server

Example: I have table TableA with 3 records:
record 1:
id = 1, value = '<Employee id='1' name='Employee1'></Employee><Employee id='2' name='Employee2'></Employee>'
record 2:
id = 2, value = '<Employee id='1' name='Employee1'></Employee><Employee id='2' name='Employee2'></Employee><Employee id='3' name='Employee3'></Employee>'
record 3:
id = 3, value = '<Employee id='1' name='Employee1'></Employee><Employee id='2' name='Employee2'></Employee><Employee id='3' name='Employee3'></Employee><Employee id='4' name='Employee4'></Employee>'
the query:
SELECT * FROM TableA
WHERE...
How can I put the where clause to get only record 1?
Many thanks,

The problem with the data is that it doesn't contain well formed xml - you will need to wrap it before you can use the xml tools in Sql like xquery.
SELECT *
FROM
(
SELECT
Nodes.node.value('(./#id)[1]', 'int') AS EmployeeId,
Nodes.node.value('(./#name)[1]', 'varchar(50)') AS EmployeeName
FROM
(
SELECT CAST('<xml>' + value + '</xml>' AS Xml) As WrappedXml
FROM TableA
) AS x
cross apply x.WrappedXml.nodes('//Employee') as Nodes(node)
) as y
WHERE
y.EmployeeId = 1;
Inner select -wraps the xml
Middle select - standard xquery
Outer select - where filter
You haven't clarified what you mean w.r.t. get only record 1, but if you mean just the first element of each row (which coincidentally also has id = 1), you can use ROW_NUMBER() to assign a sequence:
SELECT *
FROM
(
SELECT
Nodes.node.value('(./#id)[1]', 'int') AS EmployeeId,
Nodes.node.value('(./#name)[1]', 'varchar(50)') AS EmployeeName,
ROW_NUMBER() OVER (PARTITION BY x.Id ORDER BY ( SELECT 1 )) SequenceId
FROM
(
SELECT Id, CAST('<xml>' + value + '</xml>' AS Xml) As WrappedXml
FROM TableA
) AS x
cross apply x.WrappedXml.nodes('//Employee') as Nodes(node)
) as y
WHERE SequenceId = 1;
Both Fiddles here

I tried with this query and It returns record 1 as I expect:
SELECT * FROM TableA
WHERE value.exist('(Employee[#id = 1])') = 1 and value.exist('(Employee[#id = 2])') = 1 AND value.value('count(Employee[#id])', 'int') = 2
Do you have any comments for this query? Should I use it? :)

Related

How can I refer to a LAG() function column in SQL Server?

I have a query in which I use LAG function :
WITH Tr AS
(
SELECT
DocDtls.Warehouse, Transactions.Code, DocDtls.zDate,
Transactions.ID, Transactions.QtyIn, Transactions.QtyOut,
Transactions.BalanceAfter
FROM
DocDtls
INNER JOIN
Transactions ON DocDtls.[PrimDocNum] = Transactions.[DocNum]
)
SELECT
ID, Code, QtyIn, QtyOut, BalanceAfter,
LAG(BalanceAfter, 1, 0) OVER (PARTITION BY Warehouse, Code
ORDER BY Code, ID) Prev_BlncAfter
FROM
Tr;
It's working fine but when I try to add this column before FROM:
SUM(Prev_BlncAfter + QtyIn) - QtyOut AS NewBlncAfter
I get this error :
Msg 207, Level 16, State 1, Line 3
Invalid column name 'Prev_BlncAfter'
How can I fix this ? Thanks
You can create the LAG column inside the CTE instead of in the outer query. E.g.
declare #DocDtls table (Warehouse int, zDate date, [PrimDocNum] int);
declare #Transactions table (code int, id int, QtyIn int, QtyOut int, balanceafter int, [DocNum] int)
;with Tr As
(
SELECT
d.Warehouse
, t.Code
, d.zDate
, t.ID
, t.QtyIn
, t.QtyOut
, t.BalanceAfter
,LAG(BalanceAfter,1,0) Over (partition by Warehouse,Code order by Code,ID) Prev_BlncAfter
FROM #DocDtls d
INNER JOIN #Transactions t ON d.[PrimDocNum] = t.[DocNum]
)
select ID,Code,QtyIn,QtyOut,BalanceAfter
,SUM(Prev_BlncAfter + QtyIn)-QtyOut As NewBlncAfter
from Tr
group by ID,Code,QtyIn,QtyOut,BalanceAfter;
You can nest this query to refer the newly added column from the outer scope, or create another with like you've done before for referencing it afterwards:
with Tr As (
SELECT
DocDtls.Warehouse,
Transactions.Code,
DocDtls.zDate,
Transactions.ID,
Transactions.QtyIn,
Transactions.QtyOut,
Transactions.BalanceAfter
FROM
DocDtls
INNER JOIN Transactions ON DocDtls.[PrimDocNum] = Transactions.[DocNum]
),
formatted_tr as (
select
ID,
Code,
QtyIn,
QtyOut,
BalanceAfter,
LAG(BalanceAfter, 1, 0) Over (
partition by Warehouse,
Code
order by
Code,
ID
) Prev_BlncAfter
from
Tr
)
select
SUM(Prev_BlncAfter + QtyIn) - QtyOut As NewBlncAfter
from
formatted_tr
group by
ID, QtyOut
;
Based on comments , I combined the two answers to get what I need :
with Tr As (
SELECT
DocDtls.Warehouse,
Transactions.Code,
DocDtls.zDate,
Transactions.ID,
Transactions.QtyIn,
Transactions.QtyOut,
Transactions.BalanceAfter
FROM
DocDtls
INNER JOIN Transactions ON DocDtls.[PrimDocNum] = Transactions.[DocNum]
),
formatted_tr as (
select
ID,
Code,
QtyIn,
QtyOut,
BalanceAfter,
LAG(BalanceAfter, 1, 0) Over (
partition by Warehouse,
Code
order by
Code,zDate,ID
) Prev_BlncAfter
from
Tr
)
select ID,Code,QtyIn,QtyOut,BalanceAfter
,SUM(Prev_BlncAfter + QtyIn)-QtyOut As NewBlncAfter
from formatted_tr
group by ID,Code,QtyIn,QtyOut,BalanceAfter;
;

Bigquery - Transpose Array into columns

The following query returns a list of key/value pairs:
SELECT ["name:apple", "color:red"] as fruit;
result:
Is there a way to transpose the data so the results would be:
Update
I'm looking for a generalized solution, where the key and value and the array length of the results are unknown i.e: SELECT ["key0:val0", "key1:val1"...] as data;
This should be a quick way to achieve your results:
#standardSQL
with items as (
select ["name:apple", "color:red"] p union all
select ["name:orange", "color:orange"] UNION ALL
select ["name:grapes", "color:green"]
),
arrayed as (
select
array_agg(
struct(
if(split(p, ":")[offset(0)] = 'name', split(p, ":")[offset(1)], '') as name,
if(split(p, ":")[offset(0)] = 'color', split(p, ":")[offset(1)], '') as color
)
) item from items, unnest(p) p
)
select
array((select i.name from unnest(item) i where i.name != '')) as name,
array((select i.color from unnest(item) i where i.color != '')) as color
from arrayed
I guess the BigQuery way would be using sub-selects on the array:
WITH t AS (SELECT * FROM UNNEST([
struct(['name:apple','color:red'] AS fruit),
struct(['name:pear','color:purple'] AS fruit)
]) )
SELECT
(SELECT SPLIT(f, ':')[SAFE_OFFSET(1)] FROM t.fruit f WHERE SPLIT(f, ':')[SAFE_OFFSET(0)]='name') AS name,
(SELECT SPLIT(f, ':')[SAFE_OFFSET(1)] FROM t.fruit f WHERE SPLIT(f, ':')[SAFE_OFFSET(0)]='color') AS color
FROM t
Not sure if there's a more succinct way to do this but this works
WITH CTE AS (
SELECT ["name:apple", "color:red"] as fruit
UNION ALL
SELECT ["name:pear", "color:green"]
),
CTE2 AS (
SELECT row_number() over () as rowNumber, fruit
FROM CTE
)
SELECT max(if(REGEXP_CONTAINS(fruit,'name:'),replace(fruit,'name:',''),null)) name,
max(if(REGEXP_CONTAINS(fruit,'color:'),replace(fruit,'color:',''),null)) color
FROM CTE2,
UNNEST(fruit) as fruit
GROUP BY rowNumber

Adding results from two queries

I'm using MS-SQL 2008 R2.
I have 2 Queries which are returning the required results.
But I need to add the two results from each queries to provide a final value [Enterprise Value]. I'm sure this is very straight forward but I'm going round in circles on this, have tried incorporating SUM which I think is the right approach?
Here is the full query as it currently stands:
declare #d1 datetime='2015-12-22'
(select
c.fs_perm_sec_id,
((c.p_price * s.p_com_shs_out)/1000) as [Enterprise Value]
from fp_v1.fp_basic_bd c
left join edm_v1.edm_security_entity_map e
on e.fs_perm_sec_id= c.fs_perm_sec_id
left join fp_v1.fp_basic_sho s
on s.fs_perm_sec_id = c.fs_perm_sec_id
and c.date=#d1
where s."date" =
(
select MAX(s2."date")
from fp_v1.fp_basic_sho s2
where s2.fs_perm_sec_id=c.fs_perm_sec_id
and s2."date" <= c."date"
)
and c."date"=#d1
and e.termination_date is null
and c.fs_perm_sec_id = 'GPHC8W-S-GB')
UNION ALL
select
ff.fs_perm_sec_id,
((FF_debt + ff_pfd_stk + ff_min_int_accum) - FF.ff_cash_st) as [Enterprise Value]
from ff_v2.ff_basic_af_v2 FF
where FF."date" =
( select MAX(FF2."date")
from ff_v2.ff_basic_af_v2 FF2
where FF2.fs_perm_sec_id=FF.fs_perm_sec_id
and FF.date <= FF2.date
)
and FF.fs_perm_sec_id =('GPHC8W-S-GB')
When inserting a "UNION ALL" between the two queries I get the following results:
fs_perm_sec_id Enterprise Value
GPHC8W-S-GB 9270.5204655
GPHC8W-S-GB 835
What I would like to achieve is a sum of the two values brought onto one row, i.e.:
fs_perm_sec_id Enterprise Value
GPHC8W-S-GB 10105.52
Thanks for your help.
Final SQL:
declare #d1 datetime='2015-12-23'
Select fs_perm_sec_id, SUM([Enterprise Value]) AS 'Enterprise Value'
from
(
(select
c.fs_perm_sec_id,
((c.p_price * s.p_com_shs_out)/1000) as [Enterprise Value]
from fp_v1.fp_basic_bd c
left join edm_v1.edm_security_entity_map e
on e.fs_perm_sec_id= c.fs_perm_sec_id
left join fp_v1.fp_basic_sho s
on s.fs_perm_sec_id = c.fs_perm_sec_id
and c.date=#d1
where s."date" =
(
select MAX(s2."date")
from fp_v1.fp_basic_sho s2
where s2.fs_perm_sec_id=c.fs_perm_sec_id
and s2."date" <= c."date"
)
and c."date"=#d1
and e.termination_date is null
and c.fs_perm_sec_id in ('FT9TC5-S-GB','GPHC8W-S-GB','R85KLC-S-US'))
UNION ALL
select
ff.fs_perm_sec_id,
((FF_debt + ff_pfd_stk + ff_min_int_accum) - FF.ff_cash_st) as [Enterprise Value]
from ff_v2.ff_basic_af_v2 FF
where FF."date" =
( select MAX(FF2."date")
from ff_v2.ff_basic_af_v2 FF2
where FF2.fs_perm_sec_id=FF.fs_perm_sec_id
and FF.date <= FF2.date
)
and FF.fs_perm_sec_id in ('FT9TC5-S-GB','GPHC8W-S-GB','R85KLC-S-US')) t
group by t.fs_perm_sec_id
just use the Derived Table and Group by
Select fs_perm_sec_id,
SUM(Enterprise Value) EnterpriseValue
from (**your whole code**)
GROUP BY fs_perm_sec_id
use group by
How to use group by with union in t-sql
SELECT id,sum(*)
FROM ( SELECT id,
time
FROM dbo.a
UNION
SELECT id,
time
FROM dbo.b
)
GROUP BY id
DECLARE
#d1 DATE = '20151222'
, #fs_perm_sec_id VARCHAR(100) = 'GPHC8W-S-GB'
SELECT #fs_perm_sec_id, SUM([Enterprise Value])
FROM (
SELECT [Enterprise Value]
FROM (
SELECT
c.fs_perm_sec_id
, (c.p_price * s.p_com_shs_out) / 1000 AS [Enterprise Value]
, RowNum = ROW_NUMBER() OVER (ORDER BY s.[date] DESC)
from fp_v1.fp_basic_bd c
join fp_v1.fp_basic_sho s on s.fs_perm_sec_id = c.fs_perm_sec_id
left join edm_v1.edm_security_entity_map e on e.fs_perm_sec_id= c.fs_perm_sec_id
where c.[date] = #d1
and e.termination_date is null
and c.fs_perm_sec_id = #fs_perm_sec_id
) t
WHERE t.RowNum = 1
UNION ALL
SELECT FF_debt + ff_pfd_stk + ff_min_int_accum - ff_cash_st
FROM (
SELECT
ff.fs_perm_sec_id
, FF_debt
, ff_pfd_stk
, ff_min_int_accum
, FF.ff_cash_st
, RowNum = ROW_NUMBER() OVER (ORDER BY FF.[date] DESC)
FROM ff_v2.ff_basic_af_v2 FF
WHERE FF.[date] =
AND FF.fs_perm_sec_id = #fs_perm_sec_id
) t
WHERE t.RowNum = 2
) t

Need to extract only the one row using join query

I am trying to extract one record from joining the Table X and Table Y.
Below is the query am using
SELECT (select ACCOUNTSALESFORCEDETAILID
FROM sysdba.ACCOUNTSALESFORCEDETAIL AS a1
WHERE ORGSALESFORCEID = 'QWKHAA000STK'
AND ACCOUNTID = O.ACCOUNTID
and MODIFYDATE = (
SELECT MIN(MODIFYDATE) AS MODIFYDATE
FROM sysdba.ACCOUNTSALESFORCEDETAIL
WHERE ACCOUNTID = a1.ACCOUNTID
AND ORGSALESFORCEID = a1.ORGSALESFORCEID
GROUP BY ACCOUNTID, ORGSALESFORCEID
)
),
a.ORGSALESFORCEID
FROM sysdba.ACCOUNTSALESFORCEDETAIL a
JOIN sysdba.ACCOUNT O on a.ACCOUNTID=O.ACCOUNTID
WHERE O.ACCOUNTID ='A61E6C43EE71'
AND A.ORGSALESFORCEID in ('QWKHAA000STK','QWKHAA01KXNC','QWKHAA01KXNE')
Below s the result am getting now:
ACCOUNTSALESFORCEID ORGSALESFORCEID
QB46AC89800B QWKHAA000STK
QB46AC89800B QWKHAA01KXNC
QB46AC89800B QWKHAA01KXNE
But i need only First row as the output..
Please help.
Have a try like this
SELECT TOP(1) col1,ORGSALESFORCEID
FROM
(SELECT
(
SELECT
ACCOUNTSALESFORCEDETAILID
FROM
sysdba.ACCOUNTSALESFORCEDETAIL AS a1
WHERE (ORGSALESFORCEID = 'QWKHAA000STK')
AND (ACCOUNTID=O.ACCOUNTID)
AND (MODIFYDATE =( SELECT MIN(MODIFYDATE) AS MODIFYDATE
FROM sysdba.ACCOUNTSALESFORCEDETAIL
WHERE (ACCOUNTID = a1.ACCOUNTID) AND (ORGSALESFORCEID=a1.ORGSALESFORCEID)
GROUP BY ACCOUNTID, ORGSALESFORCEID)
)
) AS col1
,a.ORGSALESFORCEID
FROM
sysdba.ACCOUNTSALESFORCEDETAIL a JOIN sysdba.ACCOUNT O on a.ACCOUNTID=O.ACCOUNTID
WHERE O.ACCOUNTID='A61E6C43EE71' and A.ORGSALESFORCEID in ('QWKHAA000STK','QWKHAA01KXNC','QWKHAA01KXNE')
) AS temp

TSQL - While loop within select?

In SQL server
Ok, so I'm working with a database table in which rows can have parent rows, which can then have parent rows of their own. I need to select the root 'row'. I don't know the best way to do this.
There is a field called ParentId, which links the row to the row with that ID. When the ParentId = 0, it is the root row.
This is my query now:
SELECT Releases.Name,WorkLog.WorkLogId
FROM WorkLog,Releases
WHERE
Releases.ReleaseId = WorkLog.ReleaseId
and WorkLogDateTime >= #StartDate
and WorkLogDateTime <= #end
I don't really need the Release Name of the child releases, I want only the root Release Name, so I want to select the result of a While loop like this:
WHILE (ParentReleaseId != 0)
BEGIN
#ReleaseId = ParentReleaseId
END
Select Release.Name
where Release.RealeaseId = #ReleaseId
I know that syntax is horrible, but hopefully I'm giving you an idea of what I'm trying to acheive.
Here is an example, which could be usefull:
This query is getting a lower element of a tree, and searching up to the parent of parents.
Like I have 4 level in my table -> category 7->5, 5->3, 3-> 1. If i give it to the 5 it will find the 1, because this is the top level of the three.
(Changing the last select you can have all of the parents up on the way.)
DECLARE #ID int
SET #ID = 5;
WITH CTE_Table_1
(
ID,
Name,
ParentID
)
AS(
SELECT
ID,
Name,
ParentID
FROM Table_1
WHERE ID = #ID
UNION ALL
SELECT
T.ID,
T.Name,
T.ParentID
FROM Table_1 T
INNER JOIN CTE_Table_1 ON CTE_Table_1.ParentID = T.ID
)
SELECT * FROM CTE_Table_1 WHERE ParentID = 0
something like this
with cte as
(
select id,parent_id from t where t.id=#myStartingValue
union all
select t.id,t.parent_id
from cte
join t on cte.parent_id = t.id where cte.parent_id<>0
)
select *
from cte
join t on cte.id=t.id where cte.parent_id = 0
and with fiddle : http://sqlfiddle.com/#!3/a5fa1/1/0
Using Andras approach, I edited the final select to directly give me the ID of the root release
WITH cte_Releases
(
ReleaseId,
ParentReleaseID
)
AS(
SELECT
ReleaseId,
ParentReleaseID
FROM Releases
Where ReleaseId = 905
UNION ALL
SELECT
R.ReleaseId,
R.ParentReleaseID
FROM Releases R
INNER JOIN cte_Releases ON cte_Releases.ParentReleaseID = R.ReleaseId
)
SELECT max(ReleaseId) as ReleaseId, min(ReleaseId) as RootReleaseId FROM cte_Releases
My problem now is I want to run through all #IDs (905 in that code) and join each record to a result

Resources