Reorder nodes in SQL Hierarchy data type - sql

Using Hierarchy data type on SQL 2008. Nodes in my hierarchy go like this:
value node
36 /8/1/
38 /8/2/
34 /8/3/
40 /8/4/
42 /8/5/
44 /8/6/
46 /8/7/
48 /8/8/
I'd like to rearrange nodes so that /8/3/ and /8/1/ switch places. Any idea on how to do this?
Only idea that I have so far is that I load all nodes on a level in Array, order them the way I want them to be, delete them from table and insert in sorted form.

If (like in your example) you know the hierarchyid values you want to manipulate in advance, you can do it directly, e.g.:
-- Place 1st node between 2nd and 3rd
UPDATE yourTable SET node = CAST('/8/2.5/' AS hierarchyid) WHERE value = 36;
-- Move 3rd node to 1st
UPDATE yourTable SET node = CAST('/8/1/' AS hierarchyid) WHERE value = 34;
If you need to get your new hierarchyid values dynamically, take a look into the GetDescendant() and GetAncestor() functions. Using that, the example would look something like this:
DECLARE #Parent hierarchyid, #Child1 hierarchyid, #Child2 hierarchyid
-- Grab hierarchyids from 2nd and 3rd node
SELECT #Child1 = node FROM yourTable WHERE value = 38;
SELECT #Child2 = node FROM yourTable WHERE value = 34;
-- Get the parent hierarchyid
SELECT #Parent = #Child1.GetAncestor(1);
-- Update 1st node to end up between 2nd and 3rd
UPDATE yourTable SET node = #Parent.GetDescendant(#Child1, #Child2) WHERE value = 36;
-- Update 3rd node to end up before 2nd
UPDATE yourTable SET node = #Parent.GetDescendant(NULL, #Child1) WHERE value = 34;
Note that the hierarchyids do not stay the same in this example. At least one will end up using a 'fraction' for its position (e.g. '/8/2.5/' instead of '/8/3/').

Found the solution
http://technet.microsoft.com/en-us/library/bb677256.aspx

Related

Different child nodes in the same XML

In SQL Server 2008 there is a table with XML column to be queried and there is a need to get the name of the node that is the child of the "Inc" node - in the example below "Architect" and "Professor". We do not know all of the child nodes (Professor, Architect, etc.) so we cannot just outer apply all of the potential child nodes names, but we need to extract their values for every row.
First row:
<Inc>
<Architect>
<ArchitectLevel>
<Average>100000</Average>
</ArchitectLevel>
</Architect>
</Inc>
Second row:
<Inc>
<Professor>
<ProfessorLevel>
<Maximum>100000</Maximum>
<Minimum>1000</Minimum>
</ProfessorLevel>
</Professor>
</Inc>
Does someone now how to do it?
Use local-name() Demo
declare #x xml =
'<Inc>
<Architect>
<ArchitectLevel>
<Average>100000</Average>
</ArchitectLevel>
</Architect>
</Inc>
<Inc>
<Professor>
<ProfessorLevel>
<Maximum>100000</Maximum>
<Minimum>1000</Minimum>
</ProfessorLevel>
</Professor>
</Inc>';
select t.n.value('local-name(.)[1]','varchar(100)')
from #x.nodes('Inc/*') t(n)

SQL Server 403 Error When Setting a Geography Type for Update

All I need to do is simply get one geography value from a table and store it in another table. There is some logic for which row to take from the origin table so it's not just a straight select.
In any of 50 possible variants of this, I get this error when hitting the update to the target table:
Msg 403, Level 16, State 1, Line 1
Invalid operator for data type. Operator equals not equal to, type equals geography.
My SQL looks like this at the moment:
declare
#EquipmentId int
, #CurrentLocationId int
, #CurrentGeoLocation geography
, #LastUpdated datetime
select #EquipmentId =
(
select top 1 EquipmentId
from Equipment
order by EquipmentId
)
select #CurrentLocationId = (select top 1 EquipmentLocationId from EquipmentLocation where EquipmentId = #EquipmentId order by LastUpdated desc)
select #LastUpdated = (select top 1 LastUpdated from EquipmentLocation where EquipmentId = #EquipmentId order by LastUpdated desc)
UPDATE
dbo.Equipment
SET
CurrentLocationDateTime = #LastUpdated
, CurrentGeoLocation = (select GeoLocation from EquipmentLocation where EquipmentLocationId = #CurrentLocationId)
, ModifiedBy = 'system'
, ModifiedByUserId = -1
, ModifiedDate = getdate()
WHERE
EquipmentId = #EquipmentId
I have had CurrentGeoLocation set in a variable of the same type, selected into by the same statement you see in the update.
I have had an #CurrentGeoLocation variable populated by a geography::STGeomFromText as well as geography::Point() function call.
I've used Lat and Long variables to call Point and FromText functions.
All the same result, the above 403 error. I could understand it somewhat when I was concatenating various permutations of the GeomFromText function that needs well known text format for the point parameter, but field value to field value is killing me, as is the fact that I get this error no matter how I try to give the origin point data to the target table.
Thoughts?
Update:
I've been experimenting a little and found that the following works just fine:
declare #GL geography
select #GL = (select GeoLocation from EquipmentLocation where EquipmentLocationId = 25482766)
print convert(varchar, #GL.Lat)
print convert(varchar, #GL.Long)
update Equipment set CurrentGeoLocation = geography::Point(#GL.Lat, #GL.Long, 4326)-- #NewGL where EquipmentId = 10518
But then when I apply this plan to the original script, I'm back to the same error.
The data in the test is working off the exact same records as in the original script. The original script is working off a collection of EquipmentIds, on the first one, I encounter this problem. The short test script uses the same EquipmentLocationId and EquipemntId that are the selected values used to update the first Equipment record in my collection.
Solved!
The error had nothing to do with the geography type as SQL reported. By pulling items in and out of the update statement in an effort to isolate why I still get the error even if I save everything but CurrentGeoLocation and then another update for the geography, I found that CurrentLocationDateTime (datetime, null) was the culprit. Deleted the column, added it back. Problem solved. Original script works as expected.
Don't know what happened to that datetime column that caused it to throw errors against a geometry type, but it's fixed.

SQL, Find node value in xml variable, if it exists insert additional nodes into xml variable

I've got a Stored Procedure in SQL, where I have the following declaration:
Declare #fields xml
My SP gets passed values from the front end and then gets executed. The values it gets passed looks like this depending on what the user selects from the front end. For the purpose of this example I have included only 3 ID's.
'<F><ID>979</ID><ID>1000</ID><ID>989</ID></F>'
My question is this:
How can I find the node = 1000 and if that is present (exists) then insert (add) to 2 additional nodes,
<ID>992</ID><ID>993</ID>
to my existing '<F><ID>979</ID><ID>1000</ID><ID>989</ID></F>' xml.
If <ID>1000</ID> isn't present do nothing.
So, end result should be something like this if 1000 is present.
<F><ID>979</ID><ID>1000</ID><ID>989</ID><ID>992</ID><ID>993</ID></F>
If not, the result should stay:
<F><ID>979</ID><ID>1000</ID><ID>989</ID></F>
I just can't get my head around this?
Check this:
declare #fields xml = '<F><ID>979</ID><ID>1000</ID><ID>989</ID></F>'
, #add xml = '<ID>992</ID><ID>993</ID>'
;
if #fields.exist('/F[1]/ID[text()="1000"]') = 1
set #fields.modify('insert sql:variable("#add") as last into /F[1]');
select #fields

Update field in table for all records using a select statement

A previous developer created a table that stores the absolute path to files in our server. I want to convert them to relative paths instead.
I already wrote the portion that properly strips the string down to a relative path. My issue is understanding how to basically update each record, with a new version of its own string.
Here is what I originally tried:
UPDATE LFRX_Attachments
SET [File] = (SELECT TOP 1 SUBSTRING([File], PATINDEX('%Files\%', [File]) + 6, LEN([File]))
FROM LFRX_Attachments A
WHERE [Type] = 4 AND AttachmentId = A.AttachmentId)
However, this tanked in epic fashion by just overwriting every record to have the value of the first record in the table. Any suggestions?
UPDATE LFRX_Attachments
SET [File] = SUBSTRING([File], PATINDEX('Files\', [File]) + 6, LEN([File]))
WHERE [Type] = 4
From a readability/maintenance standpoint, you're better off selecting for the data you want to alter, then iterating through the result set and updating each record separately.
Does this work for you?
UPDATE LFRX_Attachments SET [File] = SUBSTRING([File], PATINDEX('Files\', [File]) + 6, LEN([File]))

sql to set an xml value

I'm a novice in mySql.
I'm trying to replace a value in the xml column of my table.
my select method works.
SELECT * FROM `comics` WHERE ExtractValue(xml,'comic/pageNumber') = 6
my replace method doesn't. I've been searching for the correct syntax for a bit now...
SET xml.modify(
replace value of ('comic/pageNumber') with 5
)
some background:
this situation comes up when i delete a comic page.
it leaves a gap in the page numbers, after which i would either:
iterate through all the comics and remove any gaps in the page numbers.
or
iterate through all comics with pageNumber larger than the deleted page, and reduce their pageNumber by 1.
How about
UPDATE comics
SET xml = UpdateXML(xml,'comic/pageNumber', '<pageNumber>5</pageNumber>')
WHERE ExtractValue(xml,'comic/pageNumber') = 6
Tested on MySQL version 5.1
UPDATE `comics`
SET xml = UpdateXML(xml,
'comic/pageNumber',
concat('<pageNumber>',(ExtractValue(xml,'comic/pageNumber')+1),'</pageNumber>'))
WHERE ExtractValue(xml,'comic/pageNumber') >= 1
You'd be better off actually storing the fields in the table, rather than a single field with xml in it. Then the following would work. Otherwise there's not much point using a relational database at all.
BEGIN;
DELETE FROM `comics`
WHERE `comicID` = :id AND `pageNumber` = :page;
UPDATE `comics` SET `pageNumber` = `pageNumber` - 1
WHERE `comicID` = :id AND `pageNumber` > :page;
COMMIT;