Building a custome pivot using subqueries - snowflake-cloud-data-platform

I'm trying to build my own custome pivot view where i can use multiple aggregations and more than one column to filter values on. To do that i tried to get an simple example up and running to understand how to do that. Unfortunately i'm running in an limitation i don't know how to move around.
I have to admin that im pretty new to Snowflake and this might be solvable by the existing pivot function that i just don't understand right to use.
First of all let my give you an example what i want to achive:
given a Table like so:
create temporary table test_table(PLACE varchar, TYPE varchar, METER Int, PRICE Int);
insert overwrite into test_table values
('Place_A', 'flat', 1, 10),
('Place_A', 'flat', 2, 11),
('Place_A', 'house', 3, 12),
('Place_A', 'house', 4, 13),
('Place_B', 'flat', 5, 14),
('Place_B', 'flat', 6, 15),
('Place_B', 'flat', 7, 16),
('Place_B', 'house', 8, 17),
('Place_B', 'house', 9, 18),
('Place_B', 'house', 10, 19);
i want to produce the following:
PLACE
FLAT_AVG
FLAT_MAX
HOUSE_AVG
HOUSE_MAX
And More ...
Place_A
1.5
11
3.5
13
...
Place_B
6.0
16
9.0
19
...
By using something like that
With
"AGG" As (Select PLACE, TYPE, AVG(METER) AS "AVG(METER)", MAX(PRICE) AS "MAX(PRICE)", COUNT(PRICE) From test_table Group By PLACE, TYPE)
Select
PLACE,
(Select "AVG(METER)" FROM "AGG" Where PLACE = "O".PLACE AND TYPE = 'flat') AS "FLAT_AVG",
(Select "MAX(PRICE)" FROM "AGG" Where PLACE = "O".PLACE AND TYPE = 'flat') AS "FLAT_MAX",
(Select "AVG(METER)" FROM "AGG" Where PLACE = "O".PLACE AND TYPE = 'house') AS "HOUSE_AVG",
(Select "MAX(PRICE)" FROM "AGG" Where PLACE = "O".PLACE AND TYPE = 'house') AS "HOUSE_MAX"
FROM test_table as "O" Group By PLACE
The problem is that "O".PLACE seams not to work there. I just get an Error saying:
SQL compilation error: Unsupported subquery type cannot be evaluated
I tried something similar in mariadb where a subquery like that would to the trick. I wonder what i have to do in snowflake to get something like that running?
I also tried the pivot function like so:
With
"P1" as (select *
from (Select PLACE, TYPE, METER From test_table)
pivot(AVG(METER) for TYPE in ('flat', 'house'))
as p1 (PLACE, FLAT_AVG, HOUSE_AVG)),
"P2" as (select *
from (Select PLACE, TYPE, PRICE From test_table)
pivot(MAX(PRICE) for TYPE in ('flat', 'house'))
as p1 (PLACE, FLAT_MAX, HOUSE_MAX))
Select "P1".PLACE,
FLAT_AVG, FLAT_MAX,
HOUSE_AVG, HOUSE_MAX
From "P1"
inner join "P2" on "P1".PLACE = "P2".PLACE
This did the trick for my example but like i sad in the beginning i want more than on column to filter on. How would i expand that if there was a second Column like TYPE? I have a clear idea how to do that with my first approach but not with the pivot function.
Let me know what you think i can try next.
Thanks for reading and trying to help!

SQL compilation error: Unsupported subquery type cannot be evaluated
I tried something similar in mariadb where a subquery like that would to the trick. I wonder what i have to do in snowflake to get something like that running?
Resolving correlated subquery with conditional aggregation:
With AGG As (
Select PLACE
,TYPE
,AVG(METER) AS "AVG(METER)"
,MAX(PRICE) AS "MAX(PRICE)"
,COUNT(PRICE)
From test_table
Group By PLACE, TYPE
)
Select
O.PLACE
,MAX(IFF(A.TYPE='flat', "AVG(METER)", NULL)) AS FLAT_AVG
,MAX(IFF(A.TYPE='flat', "MAX(PRICE)", NULL)) AS FLAT_MAX
,MAX(IFF(A.TYPE='house', "AVG(METER)", NULL)) AS HOUSE_AVG
,MAX(IFF(A.TYPE='house', "MAX(PRICE)", NULL)) AS HOUSE_MAX
FROM test_table as O
LEFT JOIN AGG A
ON A.PLACE = O.PLACE
GROUP BY O.PLACE;
db<>fiddle demo MariaDB

Related

Can somebody please explain this SQL LEFT JOIN behavior to get previous value?

I am trying to understand why a query behaves the way it does. I have a table with customers, revenue, sales year, and sale semester. I want to find the previous year revenue by self-joining to the table. I am well versed in using LAG and I am not looking an alternative solution, just somebody who can explain why the following works, explicitly. Here is a temp table, one customer account to keep it simple. The join is a self LEFT JOIN on customer, year, and semester. In the join it's c.SaleYear = p.SaleYear + 1. That plus 1 allows the previous record to be returned and it's just not intuitive when looking at it. If somebody could please explain how this works:
CREATE TABLE #Sales (
CustomerNumber int
,SaleYear int
,SaleSemester int
,Revenue decimal(7,2) );
INSERT INTO #Sales
SELECT 123, 2012, 1, 18.00;
INSERT INTO #Sales
SELECT 123, 2012, 1, 19.00;
INSERT INTO #Sales
SELECT 123, 2014, 3, 13.00;
INSERT INTO #Sales
SELECT 123, 2015, 2, 31.00;
INSERT INTO #Sales
SELECT 123, 2015, 3, 27.00;
INSERT INTO #Sales
SELECT 123, 2016, 2, 92.00;
INSERT INTO #Sales
SELECT 123, 2017, 3, 10.00;
-- Begin query
SELECT
c.CustomerNumber
,c.SaleYear
,c.SaleSemester
,c.Revenue AS Revenue
,ISNULL(p.Revenue, 0) AS PreviousRevenue
,p.SaleYear AS PreviousYear
,p.SaleSemester AS PreviousSemester
FROM #Sales c
LEFT JOIN #Sales p ON c.CustomerNumber = p.CustomerNumber AND c.SaleYear = p.SaleYear + 1 AND c.SaleSemester = p.SaleSemester
It's simply matching records where p.SaleYear is one LESS than c.SaleYear. That is, if p.SaleYear is 2016 and c.SaleYear is 2017, then p.SaleYear + 1 = c.SaleYear. So, you're finding the prior year's records by adding one to their year rather than subtracting one from the current year's record.
Or maybe you could think of it this way. Each condition in the where clause is essentially a pair of expressions that it is comparing:
(expression 1) = (expression 2)
If the result of that comparison is true, then it is considered a match. So in your query you have
(c.SaleYear) = (p.SaleYear + 1)
Both expressions are evaluated, and a match is found when both sides of the comparison evaluate to the same value. Or to use my previous example:
(2017) = (2016 + 1)

SUMIF greater than and Workday column

So I'm trying to convert an Excel table into SQL and I'm having difficulty coming up with the last 2 columns. Below, find my Excel table that is fully functional (in green) and a table for the code that I have in SQL so far (in yellow). I need help replicating columns C and D, I pasted the Excel formula I'm using so you can understand what I'm trying to do:
Here's the code that I have so far:
WITH
cte_DistinctScheduling AS (
SELECT DISTINCT
s.JobNo
FROM
dbo.Scheduling s
WHERE
s.WorkCntr = 'Framing')
SELECT
o.OrderNo,
o.Priority AS [P],
SUM(r.TotEstHrs)/ROUND((8*w.CapacityFactor*(w.UtilizationPct/100)),2) AS
[Work Days Left],
Cast(GetDate()+ROUND(SUM(r.TotEstHrs)/ROUND((8*w.CapacityFactor*
(w.UtilizationPct/100)),2),3) AS DATE) AS DueDate
FROM OrderDet o JOIN cte_DistinctScheduling ds ON o.JobNo = ds.JobNo
JOIN OrderRouting r ON o.JobNo = r.JobNo
JOIN WorkCntr w ON r.WorkCntr = w.ShortName
WHERE r.WorkCntr = 'Framing'
AND o.OrderNo NOT IN ('44444', '77777')
GROUP BY o.OrderNo, o.Priority, ROUND((8*w.CapacityFactor*
(w.UtilizationPct/100)),2)
ORDER BY o.Priority DESC;
My work days left column in SQL gets the right amount for that particular row, but I need it to sum itself and everything with a P value above it and then add that to today's date, while taking workdays into account. I don't see a Workday function in SQL from what I've been reading, so I'm wondering what are some creative solutions? Could perhaps a CASE statement be the answer to both of my questions? Thanks in advance
Took me a while to understand how is the Excel helpful, and I'm still having a hard time absorbing the rest, can't tell if it's a me thing or a you thing, in any case...
First, I've mocked up something to test SUM per your rationale, the idea is doing a self-JOIN and summing everything from that JOIN side, relying on the fact that NULLs will come up for anything that shouldn't be summed:
DECLARE #TABLE TABLE(P int, [Value] int)
INSERT INTO #TABLE SELECT 1, 5
INSERT INTO #TABLE SELECT 2, 6
INSERT INTO #TABLE SELECT 3, 2
INSERT INTO #TABLE SELECT 4, 4
INSERT INTO #TABLE SELECT 5, 9
SELECT T1.P, [SUM] = SUM(ISNULL(T2.[Value], 0))
FROM #TABLE AS T1
LEFT JOIN #TABLE AS T2 ON T2.P <= T1.P
GROUP BY T1.P
ORDER BY P DESC
Second, workdays is a topic that comes up regularly. In case you didn't, consider reading a little about it from previous questions, I even posted an answer on one question last week, and the thread as a whole had several references.
Thirdly, we could use table definitions and sample data loaded on SQL itself, something like I did above.
Lastly, could you please check result of UtilizationPct / 100? If that's an integer-like data type, you're probably getting a bad result on it.

Query evaluation path

Update: Had the order of the two queries wrong. First one works, the second one does not.
I'm experiencing some odd SQL Server behavior that has me stumped, and I'm hoping someone can help me figure out what's going.
My query needs to pull data from a number of tables, including the products table. The schema we work with is pretty rigid, and recently we added some new products that in their name contain a "recurrence" value (i.e. we bill the customer every 3, 6, or 12 months, and that number is only available in text, in the product name. This bites, but that's how it is).
I add a WHERE clause to filter out the 3 specific products, and then run the following piece of code to obtain the quantity recurrence-factor:
CAST(RTRIM(SUBSTRING(e.NAME, CHARINDEX('-', e.NAME) +2 , 2)) as int)
Nothing overly fancy, and with the WHERE clause specifically filtering only those 3 products, we ought to be good. I then cast the retrieved value to an integer to be used in a calculation.
However, seemingly random, the query will complain about two letter combinations not being possible to convert to an int value.
It was my assumption that by using the WHERE clause to remove unwanted products, other products would never be evaluated to begin with. If the above code is the only thing in the query, the query runs. If we add e.NAME as the only other field, I get an error:
Conversion failed when converting the nvarchar value 'ni' to data type int.
I attempted to also filter out unwanted products by hard-coding the products into the JOIN (which shouldn't really matter), but it did not help.
Execution plans and updating statistics on the table did not reveal anything worthwhile.
This throws the error:
SELECT
CAST(RTRIM(SUBSTRING(e.NAME, CHARINDEX('-', e.NAME) +2 , 2)) AS int) AS BillingFrequency
FROM
orders a
JOIN order_items b ON a.ORDER_ID = b.ORDER_ID
JOIN order_item_options c ON b.ORDER_ITEM_ID = c.ORDER_ITEM_ID
JOIN prices d ON c.PRICE_ID = d.PRICE_ID AND d.TO_OPTION_ID IN (189, 190, 191)
JOIN product_options_vw e ON d.TO_OPTION_ID = e.OPTION_ID AND e.OPTION_ID IN (189, 190, 191)
WHERE
e.OPTION_ID IN (189, 190, 191)
This works:
SELECT
e.NAME
, CAST(RTRIM(SUBSTRING(e.NAME, CHARINDEX('-', e.NAME) +2 , 2)) AS int) AS BillingFrequency
FROM
orders a
JOIN order_items b ON a.ORDER_ID = b.ORDER_ID
JOIN order_item_options c ON b.ORDER_ITEM_ID = c.ORDER_ITEM_ID
JOIN prices d ON c.PRICE_ID = d.PRICE_ID AND d.TO_OPTION_ID IN (189, 190, 191)
JOIN product_options_vw e ON d.TO_OPTION_ID = e.OPTION_ID AND e.OPTION_ID IN (189, 190, 191)
WHERE
e.OPTION_ID IN (189, 190, 191)
I see both queries are same with exception of adding one more column in second query and it is strange that the second one works for you which shouldn't be.i recommend using try cast and try below steps
Try cast throws null,if conversion fails and you can see
Try_CAST(RTRIM(SUBSTRING(e.NAME, CHARINDEX('-', e.NAME) +2 , 2)) AS int)
You can see all columns with null which might be causing the issue.further error message clearly says ..
Conversion failed when converting the nvarchar value 'ni' to data type int.
So you can try using only below part and see which one gives you an output of ni
RTRIM(SUBSTRING(e.NAME, CHARINDEX('-', e.NAME) +2 , 2))

Using the MAX function to get the latest values based on the "Creation Date/Time"

I'm trying to get the latest data from an observation table. To make sure I get the latest value, I'm using the max function on the value's "creation date/time". And to narrow my results down to a specific patient, I have to join this table to the Patient Table.
I would love to send out a visual of the table but can't since it's my first post. Anyways, see if you can figure it out without the table design.
I executed the following:
select o.FindingName, o.Value, o.CreationTime
from dbo.HObservation o inner join dbo.HPatient p
on p.ObjectID = o.Patient_oid,
(select max(CreationTime) as CT, value
from dbo.HObservation group by Value ) b
Where o.Value = b.Value and
o.CreationTime = b.CT
and o.FindingAbbr in ( 'Wt', 'A_BM', 'A_Last BM Date', 'A_BP',
'A_Pulse', 'A_Temperature', 'A_Respirations',
'A_R_SurgDate', 'A_R_ChRvORTm', 'A_R_OpPOPPreced'
, 'A_R_FNSNN', 'A_R_WOUNDNN')
and p.ObjectID = 2227268
My results appear to display all the data listed on the table above.
I tried multiple scenarios but my brain is a bit fried right now, I can't even remember them. Does anyone know what i'm doing wrong here?
Also, as you may have noticed I tried to narrow my list of "finding Names" to 12 values only. Just to make it a bit more efficient but that didn't help none.
Any ideas!!??
Is HObservation table will have records / value field for each Patients.
On your query
(select max(CreationTime) as CT, value from dbo.HObservation group by Value )
you are taking the max of time by value only. if your table has multiple patients then it will list only the max of value for those patients which is recently updated.
You can include Patient_oid column along with the value to get max (creationTime)
You left some details out of your question:
Which version of MS SQL Server you are using. I assume SQL2005 or more recent so that CTE's and the ranking function ROW_NUMBER() can be used.
Whether you want the most recent value of each observation for a given patient, or all values for the patient observed at the most recent time any observation was made for the patient. I assume the former.
This query should give you the general idea:
with WindowedObservation as (
select Patient_oid, FindingAbbr, Value, CreationTime,
row_number() over (
partition by Patient_oid, FindingAbbr
order by CreationTime desc
) as ROWNUM
from HObservation
)
select *
from WindowedObservation
join HPatient
on HPatient.ObjectId = WindowedObservation.Patient_oid
where WindowedObservation.ROWNUM = 1
Comment out the WHERE clause to see how ROW_NUMBER() is being used.
Here is a small amount of sample data to test with:
CREATE TABLE HPatient (
ObjectId int,
Name nvarchar(50)
)
insert into HPatient(ObjectId, Name)
select 1,'Patient1'
union all select 2, 'Patient2'
union all select 3,'Patient3'
CREATE TABLE HObservation (
Patient_oid int,
FindingAbbr nvarchar(50),
Value float,
CreationTime datetime
)
insert into HObservation(Patient_oid, FindingAbbr, Value, CreationTime)
select 1, 'A_Pulse', 64, '2012-01-01'
union all select 1, 'A_Pulse', 73, '2012-01-02'
union all select 1, 'A_Pulse', 59, '2012-01-03'
union all select 1, 'A_Temperature', 98.6, '2012-01-01'
union all select 1, 'A_Temperature', 98.1, '2012-01-02'
union all select 1, 'A_Temperature', 90.2, '2012-01-03'
union all select 2, 'A_Pulse', 61, '2012-01-03'
union all select 2, 'A_Pulse', 67, '2012-01-04'
union all select 2, 'A_Pulse', 64, '2012-01-05'
union all select 2, 'A_Temperature', 100.2, '2012-01-03'
union all select 2, 'A_Temperature', 98.6, '2012-01-04'
union all select 2, 'A_Temperature', 98.9, '2012-01-05'
union all select 3, 'A_Pulse', 80, '2012-02-01'
union all select 3, 'A_Temperature', 98.2, '2012-02-01'
union all select 3, 'Wt', 180, '2012-02-01'
union all select 3, 'A_Pulse', 84, '2012-02-02'
union all select 3, 'A_Respirations', 16, '2012-02-03'

SQL Server 2005: Update rows in a specified order (like ORDER BY)?

I want to update rows of a table in a specific order, like one would expect if including an ORDER BY clause, but SQL Server does not support the ORDER BY clause in UPDATE queries.
I have checked out this question which supplied a nice solution, but my query is a bit more complicated than the one specified there.
UPDATE TableA AS Parent
SET Parent.ColA = Parent.ColA + (SELECT TOP 1 Child.ColA
FROM TableA AS Child
WHERE Child.ParentColB = Parent.ColB
ORDER BY Child.Priority)
ORDER BY Parent.Depth DESC;
So, what I'm hoping that you'll notice is that a single table (TableA) contains a hierarchy of rows, wherein one row can be the parent or child of any other row. The rows need to be updated in order from the deepest child up to the root parent. This is because TableA.ColA must contain an up-to-date concatenation of its own current value with the values of its children (I realize this query only concats with one child, but that is for the sake of simplicity - the purpose of the example in this question does not necessitate any more verbosity), therefore the query must update from the bottom up.
The solution suggested in the question I noted above is as follows:
UPDATE messages
SET status=10
WHERE ID in (SELECT TOP (10) Id
FROM Table
WHERE status=0
ORDER BY priority DESC
);
The reason that I don't think I can use this solution is because I am referencing column values from the parent table inside my subquery (see WHERE Child.ParentColB = Parent.ColB), and I don't think two sibling subqueries would have access to each others' data.
So far I have only determined one way to merge that suggested solution with my current problem, and I don't think it works.
UPDATE TableA AS Parent
SET Parent.ColA = Parent.ColA + (SELECT TOP 1 Child.ColA
FROM TableA AS Child
WHERE Child.ParentColB = Parent.ColB
ORDER BY Child.Priority)
WHERE Parent.Id IN (SELECT Id
FROM TableA
ORDER BY Parent.Depth DESC);
The WHERE..IN subquery will not actually return a subset of the rows, it will just return the full list of IDs in the order that I want. However (I don't know for sure - please tell me if I'm wrong) I think that the WHERE..IN clause will not care about the order of IDs within the parentheses - it will just check the ID of the row it currently wants to update to see if it's in that list (which, they all are) in whatever order it is already trying to update... Which would just be a total waste of cycles, because it wouldn't change anything.
So, in conclusion, I have looked around and can't seem to figure out a way to update in a specified order (and included the reason I need to update in that order, because I am sure I would otherwise get the ever-so-useful "why?" answers) and I am now hitting up Stack Overflow to see if any of you gurus out there who know more about SQL than I do (which isn't saying much) know of an efficient way to do this. It's particularly important that I only use a single query to complete this action.
A long question, but I wanted to cover my bases and give you guys as much info to feed off of as possible. :)
Any thoughts?
You cannot succeed this in one query, because your updates are correlated (ie. level N depends on the updated value of level N+1). Relational engines frown on this very explicitly because of the Halloween Problem. The query plan will go out of its way to ensure that the updates occur as if they had two stages: one in which the current state was read, and then one in which the updated state was applied. If necessary, they'll spool intermediate tables just to preserve this apparent execution order (read all->write all). Since your query, if I understand correctly, tries to break this very premise I don't see any way you'll succeed.
UPDATE statements will be executed as a single query, not as a step by step result.
You need to either use a while loop/cursor (uhhgg) or maybe make use of a CTE expression view to achieve what you are trying, which gives you the recursice possibility.
Have a look at
Using Common Table Expressions
Recursive Queries Using Common Table
Expressions
Here is a one line SQL solution. If you ever relax the requirement that it need be one update statement you can factor out some of the complexity
CREATE TABLE [TableA](
[ID] [int] NOT NULL,
[ParentID] [int] NULL,
[ColA] [varchar](max) NOT NULL,
[Priority] [varchar](50) NOT NULL,
[Depth] [int] NOT NULL)
go
INSERT TableA
SELECT 1, NULL, 'p', 'Favorite', 0 UNION ALL
SELECT 2, 1, 'm', 'Favorite', 1 UNION ALL
SELECT 3, 1, 'o', 'Likeable', 1 UNION ALL
SELECT 4, 2, 'v', 'Favorite', 2 UNION ALL
SELECT 5, 2, 'v', 'Likeable', 2 UNION ALL
SELECT 6, 2, 'd', 'Likeable', 2 UNION ALL
SELECT 7, 6, 'c', 'Red-headed Stepchild', 3 UNION ALL
SELECT 8, 6, 's', 'Likeable', 3 UNION ALL
SELECT 9, 8, 'n', 'Favorite', 4 UNION ALL
SELECT 10, 6, 'c', 'Favorite', 3 UNION ALL
SELECT 11, 5, 'c', 'Favorite', 3 UNION ALL
SELECT 12, NULL, 'z', 'Favorite', 0 UNION ALL
SELECT 13, 3, 'e', 'Favorite', 2 UNION ALL
SELECT 14, 8, 'k', 'Likeable', 4 UNION ALL
SELECT 15,4, 'd', 'Favorite', 3
;WITH cte AS (
SELECT a.i, a.Depth, a.maxd, a.mind, a.maxc, a.di, a.ci, a.cdi, a.ID, a.y, CAST('' AS varchar(max))z
FROM(
SELECT DISTINCT i = 1
,p.Depth
,maxd = (SELECT MAX(Depth) FROM TableA)
,mind = (SELECT MIN(Depth) FROM TableA)
,maxc = (SELECT MAX(c) FROM (SELECT COUNT(*) OVER(PARTITION BY ParentID) FROM TableA)f(c))
,di = (SELECT MIN(Depth) FROM TableA)
,ci = 1
,cdi = (SELECT MIN(Depth) FROM TableA)
,p.ID
,CAST(p.ID AS varchar(max)) + p.ColA + SPACE(1) + CASE WHEN g IS NULL THEN '' ELSE '(' END
+ ISNULL(g,'') + CASE WHEN g IS NULL THEN '' ELSE ')' END y
FROM TableA p
LEFT JOIN TableA c ON (c.ParentID = p.ID)
CROSS APPLY (SELECT SPACE(1) + CAST(c2.ID AS varchar(max)) + ColA + SPACE(1)
FROM TableA c2 WHERE ParentID = p.ID
ORDER BY Priority
FOR XML PATH(''))f(g)
)a
UNION ALL
SELECT r.i, r.Depth, r.maxd, r.mind, r.maxc, r.di, r.ci, r.cdi, r.ID
,CASE WHEN di = cdi
THEN REPLACE(r.y,LEFT(r.z,CHARINDEX(SPACE(1),r.z,2)), r.z)
ELSE r.y END [y]
,r.z
FROM(
SELECT i = i + 1
,Depth
,[maxd]
,[mind]
,[maxc]
,CASE WHEN ci = maxc AND cdi = maxd
THEN di + 1
ELSE di
END [di]
,CASE WHEN cdi = [maxd]
THEN CASE WHEN ci + 1 > maxc
THEN 1
ELSE ci + 1
END
ELSE ci
END [ci]
,CASE WHEN cdi + 1 > maxd
THEN mind
ELSE cdi + 1
END [cdi]
,id,y
,CAST(ISNULL((SELECT y FROM(
SELECT p.Depth,p.ID
,SPACE(1) + CAST(p.ID AS varchar(max)) + p.ColA + SPACE(1) +
CASE WHEN g IS NULL THEN '' ELSE '(' END + ISNULL(g,'')
+ CASE WHEN g IS NULL THEN '' ELSE ')' END y
,r1 = DENSE_RANK() OVER(ORDER BY p.ID) --child number
,r2 = ROW_NUMBER() OVER(PARTITION BY p.ID ORDER BY p.ID) --DISTINCT not allowed in recursive section
FROM TableA p
JOIN TableA c ON (c.ParentID = p.ID)
CROSS APPLY (SELECT SPACE(1)+CAST(c2.ID AS varchar(max))+ColA+SPACE(1)
FROM TableA c2
WHERE ParentID = p.ID
ORDER BY Priority
FOR XML PATH(''))f(g)
WHERE p.Depth = cdi AND cdi < di AND p.ID <> cte.ID
)v
WHERE r1 = ci
AND r2 = 1
AND cte.y LIKE '%' + LEFT(v.y,CHARINDEX(SPACE(1),v.y,2) ) + '%'),'') AS varchar(max)) z
FROM cte
WHERE [di]<[maxd] or [ci]<[maxc] or [cdi]<[maxd]
)r
)--cte
UPDATE t
SET ColA = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE
(y,SPACE(1),''),'1',''),'2',''),'3',''),'4',''),'5',''),'6',''),'7',''),'8',''),'9',''),'0','')
FROM cte
JOIN TableA t ON (t.ID = cte.ID)
WHERE di = (SELECT MAX(Depth) FROM TableA)
AND cdi = (SELECT MAX(Depth) FROM TableA)
AND ci = (SELECT MAX(c) FROM (SELECT COUNT(*) OVER(PARTITION BY ParentID) FROM TableA)f(c))
OPTION(maxrecursion 0)
SELECT * FROM TableA
DROP TABLE TableA
JMTyler-
1 What kind of data is in ColA? What does it look like?
2 How is/should that column be originally populated? I ask this because you would only be able to run the update once since the value in that column would be modified from a previous run. Any additional runs would just concatenate more data. Which makes me believe there is another ColC with the original value for ColA (a person's name?)
3 Will a row ever be deleted orphaning it's children? If yes what should their ParentColB then point to? NULL? Does their depth then get set to 0 so they are now at the top of the hierarchy?
If you can answer this I can give you a solution
Thanks

Resources