Read XML columns in SQL - sql-server

Trying to get the values and element names extracted from one XML column.
Values are getting just in one row and not able to extract the element name.
The elements in the cell are like this:
6161...
And they are dynamically generated.
Here is the code:
SELECT mainSku, r.value('.[1]','NVARCHAR(MAX)') AS 'value', r.query('.') AS 'secondarySku'
FROM [productsMatrix]
CROSS APPLY details.nodes('/') AS x(r)
WHERE mainSku = 'TP40106'
This is the wrong actual result
This is the result that is pretended
Thanks for reading :)

Your question is far away from being clear, but my magic crystall ball is showing, that you might be looking for this:
SELECT mainSku
,r.value('text()[1]','int') AS [value]
,r.value('local-name(.)') AS [secondarySku]
FROM [productsMatrix]
CROSS APPLY details.nodes('/*') AS x(r)
WHERE mainSku = 'TP40106'
Assumptions:
Your table [productsMatrix] has got an XML column called details. This column contains an XML with no root node, just a list of XML-Elements with names like <AC486>.
The CROSS APPLY on .nodes() will return a list of all first-level-nodes, while the query picks the content (text()-node) and the element's name.

Related

Need to Add Values to Certain Items

I have a table that I need to add the same values to a whole bunch of items
(in a nut shell if the item doesn't have a UNIT of "CTN" I want to add the same values i have listed to them all)
I thought the following would work but it doesn't :(
Any idea what i am doing wrong ?
INSERT INTO ICUNIT
(UNIT,AUDTDATE,AUDTTIME,AUDTUSER,AUDTORG,CONVERSION)
VALUES ('CTN','20220509','22513927','ADMIN','AU','1')
WHERE ITEMNO In '0','etc','etc','etc'
If I understand correctly you might want to use INSERT INTO ... SELECT from original table with your condition.
INSERT INTO ICUNIT (UNIT,AUDTDATE,AUDTTIME,AUDTUSER,AUDTORG,CONVERSION)
SELECT 'CTN','20220509','22513927','ADMIN','AU','1'
FROM ICUNIT
WHERE ITEMNO In ('0','etc','etc','etc')
The query you needs starts by selecting the filtered items. So it seems something like below is your starting point
select <?> from dbo.ICUNIT as icu where icu.UNIT <> 'CTN' order by ...;
Notice the use of schema name, terminators, and table aliases - all best practices. I will guess that a given "item" can have multiple rows in this table so long as ICUNIT is unique within ITEMNO. Correct? If so, the above query won't work. So let's try slightly more complicated filtering.
select distinct icu.ITEMNO
from dbo.ICUNIT as icu
where not exists (select * from dbo.ICUNIT as ctns
where ctns.ITEMNO = icu.ITEMNO -- correlating the subquery
and ctns.UNIT = 'CTN')
order by ...;
There are other ways to do that above but that is one common way. That query will produce a resultset of all ITEMNO values in your table that do not already have a row where UNIT is "CTN". If you need to filter that for specific ITEMNO values you simply adjust the WHERE clause. If that works correctly, you can use that with your insert statement to then insert the desired rows.
insert into dbo.ICUNIT (...)
select distinct icu.ITEMNO, 'CTN', '20220509', '22513927', 'ADMIN', 'AU', '1'
from ...
;

Select Value of Parameter in Array with Denodo (VQL)

I am trying to do something that seems simple but cannot find the right syntax for Denodo's VQL (Virtual Query Language). I have a string like this: XXXX-YYYY-ZZZZ-AAAA-BBBB in a column called "location" that varies in length, and I want to get the value of the fourth set (i.e. AAAA in this example). I am using the Denodo split function like this:
SELECT SPLIT("-",location)[3] AS my_variable FROM my_table
However, the [3] doesn't work. I've tried a bunch of variations:
SELECT SPLIT("-",location)[3].value AS my_variable FROM my_table
SELECT SPLIT("-",location).column3 AS my_variable FROM my_table
etc.
Can someone please help me figure out the right syntax to return a single parameter from an array? Thank you!
SELECT field_1[3].string
FROM (SELECT split('-', 'XXXX-YYYY-ZZZZ-AAAA-BBBB') as field_1)
You have to do it using a subquery because the syntax to access the element of an array (that is, [<number>]) can only be used with field names. You cannot use something like [4] next to the result of a expression.
This question helps: https://community.denodo.com/answers/question/details?questionId=90670000000CcQPAA0
I got it working by creating a view that saves the array as a field:
CREATE OR REPLACE VIEW p_sample_data FOLDER = '/stack_overflow'
AS SELECT bv_sample_data.location AS location
, bv_sample_data.id AS id
, split('-', location) AS location_array
FROM bv_sample_data;
Notice I created a column called location_array?
Now you can use a select statement on top of your view to extract the information you want:
SELECT location, id, location_array[2].string
FROM p_sample_data
location_array[2] is the 3rd element, and the .string tells denodo you want the string value (I think that's what it does... you'd have to read more about Compound Values in the documentation: https://community.denodo.com/docs/html/browse/6.0/vdp/vql/advanced_characteristics/management_of_compound_values/management_of_compound_values )
Another way you could probably do it is by creating a view with the array, and then flattening the array, although I haven't tried that option.
Update: I tried creating a view that flattens the array, and then using an analytics (or "window") function to get a row_number() OVER (PARTITION BY id order by ID ASC), but analytic/window functions don't work against flat file sources.
So if you go the "flatten" route and your source system doesn't work with analytic fuctions, you could just go with a straight rownum() function, but you'd have to offset the value by column number you want, and then use remainder division to pull out the data you want.
Like this:
--My view with the array is called p_sample_data
CREATE OR REPLACE VIEW f_p_sample_data FOLDER = '/stack_overflow' AS
SELECT location AS location
, id AS id
, string AS string
, rownum(2) AS rownumber
FROM FLATTEN p_sample_data AS v ( v.location_array);
Now, with the rownum() function (and an offset of 2), I can use remainder division in my where clause to get the data I want:
SELECT location, id, string, rownumber
FROM f_p_sample_data
WHERE rownumber % 5 = 0
Frankly, I think the easier way is to just leave your location data in the array and extract out the nth column with the location_array[2].string syntax, where 2 is the nth column, zero based.

Parse XML for a column value in a SQL query

I've set up a SQL fiddle to mimic the tables that I currently have which can be found here: http://sqlfiddle.com/#!6/7675e/5
I have 2 tables that I would like to join (Things and ThingData) which is easy enough, but I would like 1 of the columns to be coming from a value that is pulled from parsed XML in one of the columns in ThingData.
Ideally the output would look something like this:
thingID | thingValue | xmlValue
1 | aaa | a
As you can see in the fiddle, I'm able to parse a single XML string at a time, but I'm unsure of how to go from here to using the parsing stuff as a column in a join. Any help would be greatly appreciated.
I updated your SQL Fiddle to demonstrate; the number one issue you're facing is that you're not using an XML type for your XML column. That's going to cause headaches down the road as people shove crap in that column :P
http://sqlfiddle.com/#!6/4c674/2
I have made a change to your query that gets executed .
DECLARE #xml xml
SET #xml = (select thingDataXML from ThingData where thingDataID = 3)
;WITH XMLNAMESPACES(DEFAULT 'http://www.testing.org/a/b/c/d123')
SELECT
t.[thingID]
, t.[thingValue]
, CONVERT(XML,td.[thingDataXML]).value('(/anItem/a1/b1/c1/text())[1]','Varchar(1)') as xmlValue
FROM Things t
join ThingData td
on td.[thingDataID] = t.[thingDataID]
This should join from you main table into the table containing the xml and would then retreive the one value in the xml for you, note that if you would like to return multiple values for the one row you will need to either concatinate the answer or perhaps decide on a crossjoin to then use the data with the other data set to perform some logic with.
EDIT:
The answer for your question would be that both have a use case, the cross join he showed above creates an accesible table to query which uses a tiny bit more memory while the query runs but can be accessed many times, where as the normal xquery value function just reads a node which would only work to read a single value from the xml.
Cross join would be the solution if you would like to construct tabular data out of your xml.

Order BY is not supported in view in sql server

i am trying to create a view in sql server.
create view distinct_product as
select distinct name from stg_user_dtlprod_allignmnt_vw order by name;
this is showing an error.
error message is:
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.
plz help me out where i am wrong.
You could use TOP with a number that is greater than the number of records:
CREATE VIEW [dbo].[distinct_product]
AS
SELECT DISTINCT TOP 10000000 name
FROM stg_user_dtlprod_allignmnt_vw
ORDER BY name
You cannot use TOP 100 PERCENT since the optimizer recognizes that TOP 100 PERCENT qualifies all rows and does not need to be computed at all, so the ORDER BY wouldn't be guaranteed.
A view cannot be sorted with an ORDER BY clause. You need to put the ORDER BY clause into any query that references the view.
A view is not materialized - the data isn't stored, so how could it be sorted? A view is kind of like a stored procedure that just contains a SELECT with no parameters... it doesn't hold data, it just holds the definition of the query. Since different references to the view could need data sorted in different ways, the way that you do this - just like selecting from a table, which is also an unsorted collection of rows, by definition - is to include the order by on the outer query.
You can't order a view like that when it's created as the message states, unless you follow the other answers from Tim / Raphael, but you can order results selected from a view:
So create it in step 1:
create view distinct_product as
select distinct name
from stg_user_dtlprod_allignmnt_vw
Then order it when you retrieve data:
select *
from distinct_product
order by name

How do I build XQuerys to include/exclude rows based on the presence of certain tags and attributes?

I have an auditing/logging system that uses raw XML to represent actions taken out by an application. I'd like to improve on this system greatly by using an XML column in a table in the application's SQL Server database.
Each row in the table would contain one log entry and each entry should contain one or more tags that are used to describe the action in a semantic fashion that allows me to search in ways that match the auditing needs of the application, example:
<updateInvoice id="5" userId="7" /><fieldUpdate name="InvoiceDate" /><invoice /><update />
<deleteInvoice id="5" userId="6" /><invoice /><delete />
My intention is to return rowsets from this table by specifying combinations of tags and attributes to include or exclude rows by (e.g. "Include all rows with the tag invoice but exclude rows with the attribute userId='7'", or "Include all rows with the tag invoice but exclude rows with the tag delete)
I wish to do so programatically by using combinations of a simple filter structure to represent combinations of tags and attributes that I want to cause rows to be either included or excluded.
The structure I use looks like this:
enum FilterInclusion { Include, Exclude };
public struct Filter
{
FilterInclusion Inclusion;
string TagName;
string? AttributeName;
object? AttributeValue;
}
My goal is to accept a set of these and generate a query that returns any rows that match any single inclusion filter, without matching any single exclusion filter.
Should I and can I encode this boolean logic into the resulting XPath itself, or am I looking at having multiple SELECT statements in my outputted queries? I'm new to XQuery and any help is appreciated. Thanks!
I'm not sure if that's what you're looking for, but to filter nodes in XML methods you use the brackets [ and ]. For instance to select the elements foo but filter to only those that have the attribute bar you'd use an XPath like /foo[#bar]. If you want those that have the attribute #bar with value 5 you use /foo[#bar=5]. If you want to select the elements foo that have a child element bar you use /foo[bar].
declare #t table (x xml);
insert into #t (x) values
(N'<foo bar="abc"/>');
insert into #t (x) values
(N'<foo bar="5"/>');
insert into #t (x) values
(N'<foo id="1"><bar id="2"/></foo>');
select * from #t;
select c.value(N'#bar', N'varchar(max)')
from #t cross apply x.nodes(N'/foo[#bar]') t(c)
select c.value(N'#bar', N'varchar(max)')
from #t cross apply x.nodes(N'/foo[#bar=5]') t(c)
select c.value(N'#id', N'int')
from #t cross apply x.nodes(N'/foo[bar]') t(c)
I tried to show the examples on the XML snippets in your post, but there those are too structureless to make useful examples.

Resources