PostgresSQL v12.5
There is a table with single column containing strings formatted as XML.
create table XMLDATA (value text);
--------
text
--------
<something> <a>uyt</a> <b>xyz</b> </something>
<something> <a>ryu</a> <b>sdg</b> </something>
For simplicity let's claim that there are no nesting: all tags inside <something> contain primitive values (strings).
Assuming that there are much more elements than <a> and <b> inside, it would be great to have an option to convert these values into a relational form without enumerating all of the nested tags manually.
Was trying to get something in documentation related to XPATH, XMLTABLE, XPATH_TABLE, but there are small number of examples that did not help me to reveal the full power of these functions.
What I am looking for is a function special_function with results like
select * from special_function(XMLDATA);
a | b
-----------
uyt | xyz
ryu | sdg
Could you help me find a functionality of PostgreSQL that automatically recognizes XML tags and convert their content into columns?
without enumerating all of the nested tags manually.
That's not possible.
One fundamental restriction of SQL is, that the number, data types and names of all columns need to be known to the database before the query starts executing. SQL can't do this "at runtime" and change structure of the query based on data that is retrieved.
You can extract the content using xmltable() - but as explained, there is no way without specifying each output column.
select x.*
from xmldata d
cross join xmltable('/something' passing d.value
columns a text path 'a',
b text path 'b') as x
This assumes value is declared with the data type xml (which it should be). If it's not the case, you need to cast it: passing d.value::xml
Related
I've been trying for days now to retrieve data from an XML file with a SELECT statement in SQL Developer but I constantly get the 'ORA-00904' when trying to execute the statement. So far, these are the steps I've been following:
Create the table and directory where I want the XML data to be stored in:
CREATE TABLE PLAYER OF XMLTYPE; / CREATE DIRECTORY PLDIR AS 'C:\Users\marta\OneDrive\Escritorio\UOC\Sem3\ABD\PR2'; /
Insert into my PLAYER table said data:
INSERT INTO PLAYER VALUES (XMLTYPE(bfilename('PLDIR', 'InfoPlayersWPT.xml'),nls_charset_id('AL32UTF8')))
/
So far so good. The issue appears when I try to execute the SELECT statement
What could it be? I've changed the $Name parameter a million times as well as the Name field but nothing changes. The thing is that in the XML file, these are the fields:
--Update--
I've modified a little bit the structure and this is the new error I get:
enter image description here
I've reached a point where I don't get if there could be a problem with my database connection or if the variable are incorrect.
Any form of help would be much appreciated.
Your table doesn't have a name or id column. Your query is trying to get those, while also transforming the XML to an info node making the id a node rather than an attribute, but you still don't extract the values from that new XML. You don't need to though.
If the document only has one player (which seems unlikely with the outer node 'Players') then you can get the data with an XMLQuery call for each value:
select XMLQuery('/Players/Player/#id' passing p.object_value returning content) as id,
XMLQuery('/Players/Player/Name/text()' passing p.object_value returning content) as name
from player p
ID
NAME
1
Francinsco Navarro Compán
But it's a bit simpler, particularly if you add more data items, to use a single XMLTable instead:
select x.id, x.name
from player p
cross apply XMLTable(
'/Players/Player'
passing p.object_value
columns id number path '#id',
name varchar2(30) path 'Name'
) x
ID
NAME
1
Francinsco Navarro Compán
fiddle
... which would also handle multiple players in each XML document.
I have a Orable Table with one CLOB column which contains JSON data. I need a query which will search within the CLOB data.
I have used the condition where DBMS_LOB.instr(colName,'apple:')>0 which gives the records having apple:. However, I need to the query to return records with any number of apples other than blank, meaning, the json apple key should have a value.
I am thinking of something like where DBMS_LOB.instr(colName,'apple:**X**')>0, where X can be any number not null. I tried regexp_instr but it seems this is not correct for CLOB.
Are there any alternatives to solve this?
Generic string functions for parsing JSON inputs are dangerous - you will get false positives, for example, when something that looks like a JSON object is in fact embedded in a string value. (Illustrated by ID = 101 in my example below.)
The ideal scenario is that you are using Oracle 19 or higher; in that case you can use a simple call to json_exists as illustrated below. In the sample table I create, the first JSON string does not contain a member named apple. In the second row, the string does contain a member apple but the value is null. The first query I show (looking for all JSON with an apple member) will include this row in the output. The last query is what you need: it adds a filter so that a JSON string must include at least one apple member with non-null value (regardless of whether it also includes other members named apple, possibly with null value).
create table sample_data
( id number primary key
, colname clob check (colname is json)
);
insert into sample_data
values (101, '{name:"Chen", age:83, values:["{apple:6}", "street"]}');
insert into sample_data
values (102, '{data: {fruits: [{orange:33}, {apple:null}, {plum:44}]}}');
insert into sample_data
values (103, '[{po:3, "prods":[{"apple":4}, {"banana":null}]},
{po:4, "prods":null}]');
Note that I intentionally mixed together quoted and unquoted member names, to verify that the queries below work correctly in all cases. (Remember also that member names in JSON are case sensitive, even in Oracle!)
select id
from sample_data
where json_exists(colname, '$..apple')
;
ID
---
102
103
This is the query you need. Notice the .. in the path (meaning - find an object member named apple anywhere in the JSON) and the filter at the end.
select id
from sample_data
where json_exists(colname, '$..apple?(# != null)')
;
ID
---
103
You can use regexp_like function for this:
where regexp_like(colName,'apple : [0-9]')
For example I have table "BigApple" with three columns.
first column includes numbers
second column includes some text
third column includes XML files.
My question is: how to get to the third column of the specific values for a particular tag?
Use one of the XML methods on XML column https://msdn.microsoft.com/en-us/library/ms190798.aspx
In fact, if you have the same kind of XML data in the third column you can read specific tag values easily.
Please refer to examples on SQL XML query using a single XML variable
and example to query XML column in SQL database table using CROSS APPLY
Mao, how do you expect to get an answer which really helps you without showing your data? It can be trivial 'til really tricky to get data from an XML. Do you need only one particular tag? Or are there several data? Nested data?
One example for a trivial read might be this:
CREATE TABLE #tmpTbl(Number INT, SomeText VARCHAR(100),SomeXML XML);
INSERT INTO #tmpTbl VALUES
(1,'Test1','<root><a>xmlA1</a><b>xmlB1</b></root>')
,(2,'Test2','<root><a>xmlA2</a><b>xmlB2</b></root>');
SELECT Number
,SomeText
,SomeXML.value('(/root/a)[1]','varchar(10)') AS Tag_a
FROM #tmpTbl;
GO
DROP TABLE #tmpTbl;
The result
Number SomeText Tag_a
1 Test1 xmlA1
2 Test2 xmlA2
I have a database column containing a string that might look something like this u/1u/3u/19/g1/g4 for a particular row.
Is there a performant way to get all rows that have at least one of the following elements ['u/3', 'g4'] in that column?
I know I can use AND clauses, but the number of elements to verify against varies and could become large..
I am using RoR/ActiveRecord in my project.
in sql server, you can use XML to convert your list of search params into a record set, then cross join that with the base table, and do charIndex() to see if the column contains the substring.
Since i don't know your table or column names, i used a table (persons) that i already had data in, which has a column 'phone_home'. To search for any phone number that contains '202' or '785' i would use this query:
select person_id,phone_home,Split.data.value('.', 'VARCHAR(10)')
from (select *, cast('<n>202</n><n>785</n>' as XML) as myXML
from persons) as data cross apply myXML.nodes('/n') as Split(data)
where charindex(Split.data.value('.', 'VARCHAR(10)'),data.phone_Home) > 0
you will get duplicate records if it matches more than one value, so throw a distinct in there and remove the Split from the select statement if that is not desired.
Using xml in sql is voodoo magic to me...i got the idea from this post http://www.sqljason.com/2010/05/converting-single-comma-separated-row.html
no idea what performance is like...but at least there aren't any cursors or dynamic sql.
EDIT: Casting the XML is pretty slow, so i made it a variable so it only gets cast once.
declare #xml XML
set #xml = cast('<n>202</n><n>785</n>' as XML)
select person_id,phone_home,Split.persons.value('.', 'VARCHAR(10)')
from persons cross apply #xml.nodes('/n') as Split(persons)
where charindex(Split.persons.value('.', 'VARCHAR(10)'),phone_Home) > 0
In database table in Informix I have columns like this:
Table name is MYTABLE
key 1234
value 'POCO','LOCD',MACD'
Now I want to use this in a query like this
select * from table where symbol in (select value from MYTABLE where key='1234');
But this query is not working as value is stored as char and
output of select value from MYTABLE where key='1234' would be something like
"'POCO','LOCD',MACD'"
Is there a way to make this work. I want to achieve this in a single query.
Please suggest a better approach.
You cannot interpolate values like that. The database optimiser cannot be expected to know that value is going to return a string that looks like a list, which is to be interpreted in a list context.
Since you ask for a better approach…
That design breaks numerous fundamental rules about how databases should be structured. At a minimum, the 'value' column should be a COLLECTION data type, so that its role as a list of values is properly articulated. Personally I would create a standard, relational bridging table:
MYTABLE
key col1 col2
1234 .. ..
MYVALUE
key value
1234 POCO
1234 LOCD
1234 MACD
This is not the easy way out suggested by others, but it is the right answer.