XML default namespace and prefixed attributes - sql

I stumbled over some odd behaviour in connection with prefixed attributes. I know - in general - that in most cases prefixed attributes are not used, as they belong to an element which is in a namespace already. But shortly I tried to answer a question and found something I could not understand, even after some research:
DECLARE #xml XML=
N'<test:root xmlns:test="SomeURL">
<test:SomeElement test:SomeAttribute="yeah!" />
</test:root>';
I can wildcard the namespace:
SELECT #xml.value(N'(/*:root/*:SomeElement/#*:SomeAttribute)[1]',N'nvarchar(max)');
I can give an alias to the namespace:
WITH XMLNAMESPACES('SomeURL' AS ns)
SELECT #xml.value(N'(/ns:root/ns:SomeElement/#ns:SomeAttribute)[1]',N'nvarchar(max)');
I thought - as there is only one namespace - I could use it as default:
WITH XMLNAMESPACES(DEFAULT 'SomeURL')
SELECT #xml.value(N'(/root/SomeElement/#SomeAttribute)[1]',N'nvarchar(max)'); --fails!
If I use the same as above but set a wildcard to the attribute it works:
WITH XMLNAMESPACES(DEFAULT 'SomeURL')
SELECT #xml.value(N'(/root/SomeElement/#*:SomeAttribute)[1]',N'nvarchar(max)');
When I use old fashioned FROM OPENXML I see, that the attribute is a member of test / SomeURL namespace, just as all elements:
DECLARE #hdoc INT;
EXEC sp_xml_preparedocument #hdoc OUTPUT, #xml;
SELECT * FROM OPENXML (#hdoc, '//*',3);
EXEC sp_xml_removedocument #hdoc;
The result:
+----+----------+----------+---------------+--------+--------------+----------+------+---------+
| id | parentid | nodetype | localname | prefix | namespaceuri | datatype | prev | text |
+----+----------+----------+---------------+--------+--------------+----------+------+---------+
| 0 | NULL | 1 | root | test | SomeURL | NULL | NULL | NULL |
+----+----------+----------+---------------+--------+--------------+----------+------+---------+
| 2 | 0 | 2 | test | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+---------------+--------+--------------+----------+------+---------+
| 5 | 2 | 3 | #text | NULL | NULL | NULL | NULL | SomeURL |
+----+----------+----------+---------------+--------+--------------+----------+------+---------+
| 3 | 0 | 1 | SomeElement | test | SomeURL | NULL | NULL | NULL |
+----+----------+----------+---------------+--------+--------------+----------+------+---------+
| 4 | 3 | 2 | SomeAttribute | test | SomeURL | NULL | NULL | NULL |
+----+----------+----------+---------------+--------+--------------+----------+------+---------+
| 6 | 4 | 3 | #text | NULL | NULL | NULL | NULL | yeah! |
+----+----------+----------+---------------+--------+--------------+----------+------+---------+
This is a tiny issue, as it is easy to find a work around, but I'm curious...
Any light on this?

In XPath, as in XML, the default namespace does not apply to attributes. An element name written without a prefix assumes the default namespace, while an attribute name written without a prefix assumes "no namespace".

Related

Shifting hierarchyid set

I have a table that contains a set of values and a hierarchyid column. Looks something like this:
+-----+-------------+-----------+
| ID | HierarchyID | Name | HierarchyID.ToString() for clarity
+-----+-------------+-----------+
| 1 | 0x58 | Testing | /1/
| 2 | 0x5AC0 | TChild1 | /1/1
| 3 | 0x5AD6 | TChild1.1 | /1/1/1
| 4 | 0x5ADA | TChild1.2 | /1/1/2/
| 5 | 0x68 | Example | /2/
| 6 | 0x6AC0 | EChild1 | /2/1
| ... | ... | ... |
+-----+-------------+-----------+
However, we are introducing a new data set of that aligns side by side with the current tree and I'll need to shift all the values in my current tree down a level so it should look something like this now.
+-----+-------------+-----------+
| ID | HierarchyID | Name | HierarchyID.ToString() for clarity
+-----+-------------+-----------+
| | 0x58 | OldData | /1/
| 1 | 0x5AC0 | Testing | /1/1/
| 2 | 0x5AC6 | TChild1 | /1/1/1
| 3 | 0x5AD6B0 | TChild1.1 | /1/1/1/1
| 4 | 0x5AD6D0 | TChild1.2 | /1/1/1/2/
| 5 | 0x5B40 | Example | /1/2/
| 6 | 0x5B56 | EChild1 | /1/2/1
| 6 | 0x68 | NewData | /2
| 6 | 0x6AC0 | NChild1 | /2/1
| ... | ... | ... |
+-----+-------------+-----------+
Is there an easy way to update all of my hierarchyid values to shift them down a level or do I have to update each row one by one without overlapping values on updates?
Just from looking up the documentation.
There is an easy way to move a subtree.
Working with hierarchyid Data
Under Moving Subtree there is an example for an Employee hierarchy.
You will have to adjust to your table structure.
CREATE PROCEDURE MoveOrg(#oldMgr nvarchar(256), #newMgr nvarchar(256) )
AS
BEGIN
DECLARE #nold hierarchyid, #nnew hierarchyid
SELECT #nold = OrgNode FROM HumanResources.EmployeeDemo WHERE LoginID = #oldMgr ;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
SELECT #nnew = OrgNode FROM HumanResources.EmployeeDemo WHERE LoginID = #newMgr ;
SELECT #nnew = #nnew.GetDescendant(max(OrgNode), NULL)
FROM HumanResources.EmployeeDemo WHERE OrgNode.GetAncestor(1)=#nnew ;
UPDATE HumanResources.EmployeeDemo
SET OrgNode = OrgNode.GetReparentedValue(#nold, #nnew)
WHERE OrgNode.IsDescendantOf(#nold) = 1 ;
COMMIT TRANSACTION
END ;
GO

SQL for calculated column that chooses from value in own row

I have a table in which several indentifiers of a person may be stored. In this table I would like to create a single calculated identifier column that stores the best identifier for that record depending on what identifiers are available.
For example (some fictional sample data) ....
Table = "Citizens"
Id | LastName | DL-No | SS-No | State-Id-No | Calculated
------------------------------------------------------------------------
1 | Smith | NULL | 374-784-8888 | 7383204848 | ?
2 | Jones | JG892435262 | NULL | NULL | ?
3 | Trask | TSK73948379 | NULL | 9276542119 | ?
4 | Clinton | CL231429888 | 543-123-5555 | 1840430324 | ?
I know the order in which I would like choose identifiers ...
Drivers-License-No
Social-Security-No
State-Id-No
So I would like the calculated identifier column to be part of the table schema. The desired results would be ...
Id | LastName | DL-No | SS-No | State-Id-No | Calculated
------------------------------------------------------------------------
1 | Smith | NULL | 374-784-8888 | 7383204848 | 374-784-8888
2 | Jones | JG892435262 | NULL | 4537409273 | JG892435262
3 | Trask | NULL | NULL | 9276542119 | 9276542119
4 | Clinton | CL231429888 | 543-123-5555 | 1840430324 | CL231429888
IS this possible? If so what SQL would I use to calculate what goes in the "Calculated" column?
I was thinking of something like ..
SELECT
CASE
WHEN ([DL-No] is NOT NULL) THEN [DL-No]
WHEN ([SS-No] is NOT NULL) THEN [SS-No]
WHEN ([State-Id-No] is NOT NULL) THEN [State-Id-No]
AS "Calculated"
END
FROM Citizens
The easiest solution is to use coalesce():
select c.*,
coalesce([DL-No], [SS-No], [State-ID-No]) as calculated
from citizens c
However, I think your case statement will also work, if you fix the syntax to use when rather than where.

Joining two tables and show data from one if there is any

I have these two tables that i need to join
fields_data fields
+------------+-----------+------+ +------+-------------+----------+
| relationid | fieldname | data | | name | displayname | position |
+------------+-----------+------+ +------+-------------+----------+
| 2 | ftp | test | | user | Username | top |
| 2 | other | 1234 | | pass | Password | top |
+------------+-----------+------+ | ftp | FTP | top |
| log | Log | top |
| txt | Text | mid |
+------+-------------+----------+
I want to get all the rows from the "fields" table if they have the position "top" AND if a row has a match on name = fieldname from fields_data it should also show the data. This is my join
SELECT
fd.`data`,
fd.`relationid`,
fd.`fieldname`,
f.`name`,
f.`displayname`
FROM `fields` AS f
LEFT OUTER JOIN `fields_data` AS fd
ON fd.`fieldname` = f.`name`
WHERE f.`position`='top' AND (fd.`relationid`='3' OR fd.`relationid` IS NULL)
My problem is that the above query only gives me this result:
+------+------------+-----------+------+-------------+
| data | relationid | fieldname | name | displayname |
+------+------------+-----------+------+-------------+
| NULL | NULL | NULL | user | Username |
| NULL | NULL | NULL | pass | Password |
| NULL | NULL | NULL | log | Log |
+------+------------+-----------+------+-------------+
The field called "ftp" is missing due to it having a relation to "2".. However i still want to display it as result but like the others with NULL in it. And if the SQL query had "fd.relationid='2'" instead of 3 it would give same result, but with the row containing ftp in name, holding data in the three fields.
I hope you get what i mean.. My english is not the best.. Heres the result i want:
with above query containing fd.`relationid`='3'
+------+------------+-----------+------+-------------+
| data | relationid | fieldname | name | displayname |
+------+------------+-----------+------+-------------+
| NULL | NULL | NULL | user | Username |
| NULL | NULL | NULL | pass | Password |
| NULL | NULL | NULL | ftp | FTP |
| NULL | NULL | NULL | log | Log |
+------+------------+-----------+------+-------------+
with above query containing fd.`relationid`='2'
+------+------------+-----------+------+-------------+
| data | relationid | fieldname | name | displayname |
+------+------------+-----------+------+-------------+
| NULL | NULL | NULL | user | Username |
| NULL | NULL | NULL | pass | Password |
| test | 2 | ftp | ftp | FTP |
| NULL | NULL | NULL | log | Log |
+------+------------+-----------+------+-------------+
You want to move the condition to the on clause:
SELECT fd.`data`, fd.`relationid`, fd.`fieldname`, f.`name`, f.`displayname`
FROM `fields` f LEFT OUTER JOIN
`fields_data` fd
ON fd.`fieldname` = f.`name` AND fd.`relationid` = '3'
WHERE f.`position`='top' ;
It is interesting that the semantics of your query and this query are different -- and you found the exact situation: when there is a match on another value, the where clause form filters out the row. This will still keep everything.
As a note, the following also does what you want:
SELECT fd.`data`, fd.`relationid`, fd.`fieldname`, f.`name`, f.`displayname`
FROM `fields` f LEFT OUTER JOIN
(SELECT fd.*
FROM `fields_data` fd
WHERE fd.`relationid` = '3'
) fd
ON fd.`fieldname` = f.`name`
WHERE f.`position` = 'top' ;
I wouldn't recommend writing the query this way, particularly in MySQL (because the subquery is materialized). However, understanding why your version is different from these versions (and why these are the same) is a big step forward in mastering outer joins.

How to execute my query in hibernate

My table description is
desc sensor_log_history;
+-------------+-----------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-----------------+------+-----+-------------------+-----------------------------+
| AutoPk | int(4) unsigned | NO | MUL | NULL | |
| sensorName | varchar(20) | NO | | NULL | |
| SensorValue | double(65,2) | NO | | 0.00 | |
| timest | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------------+-----------------+------+-----+-------------------+-----------------------------+
4 rows in set (0.00 sec)
My query is
SELECT AutoPk,Result1.sensorName, SensorValue,Result1.timest FROM (SELECT MAX(timest) AS timest, sensorName FROM sensor_log_history GROUP BY sensorName) AS Result1 INNER JOIN sensor_log_history ON Result1.timest = sensor_log_history.timest WHERE Result1.sensorName = sensor_log_history.sensorName;
How to execute this query using hibernate. Hibernate should return a list of objects of type sensor_log_history.?
Simply create a POJO in your JAVA application and map this table to that POJO using hibernate. Then fire HQL query : "FROM ".

BadSQLGrammarException -- where's the error?

I am getting a BadSQLGrammarException, but I cannot see where the error is in the following SQL statement:
insert into comment(comment_date, name, comment) values '4/27/2013', 'Frank', 'Test';
Here is the description of my table. (Yes, I am converting a date into a String. There are probably better ways to do it with SQL, but that's not the question here.)
+--------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| comment_date | varchar(10) | YES | | NULL | |
| name | varchar(200) | YES | | NULL | |
| comment | varchar(200) | YES | | NULL | |
+--------------+--------------+------+-----+---------+-------+
INSERT INTO table (fieldlist) VALUES (valueslist)
^-- ^--
you forgot the indicated brackets.
Try using parentheses around the values, like this:
insert into comment(comment_date, name, comment) values ('4/27/2013', 'Frank', 'Test');