Hopefully this isn't too silly of a question. I've been trying to find an answer for a bit now.
Suppose I have a table like so:
MyTable:
Name NVARCHAR(25)
Data XML
How can I select the entire column value as XML?
declare #XmlVar xml = (select top 1 Data from MyTable)
Does not seem to allow manipulation of that xml later on.
declare #XmlVar xml = (select top 1 Data from MyTable FOR XML AUTO)
Seems to append additional nodes (<MyTable><Data>...)
It turns out I was not specifying the namespace required, and thus the Modify statement was not working.
So:
declare #XmlVar xml = (select top 1 Data from MyTable)
works fine.
#XmlVar.modify('declare default element namespace "MyNameSpaceUrl";
delete /RootNode[1]')
works when I specify the default element namespace as descibed in MyTable.Data.
Hope this saves someone else some time in the future.
Related
I have a database that contains a table. wherein one of the columns in that table is [database_name]. I want to use the contents of that column to link to a table in another database and return the contents of one of the columns in that table.
Here's what I'm trying to do, but it doesn't work.
SELECT t1.col1
,t1.col2
,t1.col3
,t1.database_name
,t1.cust_id
,(SELECT t2.ordered_on
FROM [t1.database_name].[dbo].[order_info] t2
WHERE t2.cust_id = t1.cust_id
) as order_placed_on
FROM [my_database].[dbo].[table1] t1
ORDER BY order_placed_on DESC
The problem, of course, is that SQL Server takes the [t1.data_source] thing literally, and doesn't substitute the CONTENTS of the column t1.data_source.
I also tried declaring a variable #databaseName and using #databaseName.[dbo].[order_info] but I can't seem to get that to work either. Here's the example of that non-working code:
DECLARE #databaseName nvarchar(50)
SELECT t1.col1
,t1.col2
,t1.col3
,t1.database_name
,#databaseName = t1.database_name
,t1.cust_id
,(SELECT t2.ordered_on
FROM #databaseName.[dbo].[order_info] t2
WHERE t2.cust_id = t1.cust_id
) as order_placed_on
FROM [my_database].[dbo].[table1] t1
ORDER BY order_placed_on DESC
That code gives me
Incorrect syntax near '.'"
Clearly it doesn't like the #databaseName.[dbo].[order_info] format. And if I add brackets around the #dataSource variable, so it looks like [#databaseName].[dbo].[order_info] I get a "A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations." error. No clue why. select #variableName = column_name is precisely how you assign data to a variable in a select statement. So I have no idea what's going on there... Something is getting it confused, but I don't know what, and at this point, I just need this stupid thing to work.
Anyway, I just need to know how to make SQL Server substitute the CONTENTS of the column for the column name (or variable name) in that second select's from statement.
As per my requirement, I have to find if some words like xyz#test.com value exists in which tables of columns. The database size is very huge and more than 2500 tables.
Can anyone please provide an optimal way to find this type of value from the database. I've created a loop query which took around almost more than 9 hrs to run.
9 hours is clearly a long time. Furthermore, 2,500 tables seems close to insanity for me.
Here is one approach that will run 1 query per table, not one per column. Now I have no idea how this will perform against 2,500 tables. I suspect it may be horrible. That said I would strongly suggest a test filter first like Table_Name like 'OD%'
Example
Declare #Search varchar(max) = 'cappelletti' -- Exact match '"cappelletti"'
Create Table #Temp (TableName varchar(500),RecordData xml)
Declare #SQL varchar(max) = ''
Select #SQL = #SQL+ ';Insert Into #Temp Select TableName='''+concat(quotename(Table_Schema),'.',quotename(table_name))+''',RecordData = (Select A.* for XML RAW) From '+concat(quotename(Table_Schema),'.',quotename(table_name))+' A Where (Select A.* for XML RAW) like ''%'+#Search+'%'''+char(10)
From INFORMATION_SCHEMA.Tables
Where Table_Type ='BASE TABLE'
and Table_Name like 'OD%' -- **** Would REALLY Recommend a REASONABLE Filter *** --
Exec(#SQL)
Select A.TableName
,B.*
,A.RecordData
From #Temp A
Cross Apply (
Select ColumnName = a.value('local-name(.)','varchar(100)')
,Value = a.value('.','varchar(max)')
From A.RecordData.nodes('/row') as C1(n)
Cross Apply C1.n.nodes('./#*') as C2(a)
Where a.value('.','varchar(max)') Like '%'+#Search+'%'
) B
Drop Table #Temp
Returns
If it Helps, the individual queries would look like this
Select TableName='[dbo].[OD]'
,RecordData= (Select A.* for XML RAW)
From [dbo].[OD] A
Where (Select A.* for XML RAW) like '%cappelletti%'
On a side-note, you can search numeric data and even dates.
Make a procedure with VARCHAR datatype of column with table name and store into the temp table from system tables.
Now make one dynamic Query with executing a LOOP on each record with = condition with input parameter of email address.
If condition is matched in any statement using IF EXISTS statement, then store that table name and column name in another temp table. and retrieve the list of those records from temp table at end of the execution.
I'm relatively new to SQL and I'm trying to write a query that will assign the result of multiple rows into a local variable
DECLARE #x VARCHAR(MAX)
SET #x= (SELECT someCol
FROM table)
--Does important stuff to the #x variable
SELECT #x
During my research I realized that this won't work because the subquery can only return one value and my query will return multiple results. However I can not do something like this:
DECLARE #x VARCHAR(MAX)
SET #x= (SELECT someCol
FROM table
where id= 'uniqueIdentifier')
--Does important stuff to the #x variable
SELECT #x
The reason I can't use a where clause is that I need to do this for the entire table and not just one row. Any ideas?
EDIT: I realized my question was too broad so I'll try to reformat the code to give some context
SELECT col_ID, col_Definition
FROM myTable
If I were to run this query col_Definition would return a large varchar which holds a lot of information such as the primary key of another table that I'm trying to obtain. Lets say for example I did:
DECLARE #x VARCHAR(MAX)
SET #x= (SELECT col_Definition
FROM myTable
WHERE col_ID = 1)
--Function to do the filtering of the varchar variable that works as expected
SELECT #x as [Pk of another table] --returns filtered col_Definition
This will work as expected because it returns a single row. However, I would like to be able to run this query so that it will return the filtered varchar for every single row in the "myTable" table.
If I understand correctly, you store a PK embedded in a string that you want to eventually get out and use to join to that table. I would think putting the group of records you want to work with into a temp table and then applying some logic to that varchar column to get the PK. That logic is best as set based, but if you really want row by row, use a scalar function rather than a variable and apply it to the temp table:
select pk_column, dbo.scalarfunction(pk_column) as RowByRowWithFunction, substring(pk_column,5,10) as SetBasedMuchFaster
from #tempTable.
You need to define what the 'uniqueIdentifier' is first.
Not sure about using a subquery and grabbing the result and executing another query with that result unless you do an INNER JOIN of some sort or if using python or another language to process the data then use:
("SELECT someCol
FROM table
where id='%s'" % Id)
I have a column that contains XML data, but is TEXT type and not XML type. (I have to leave it like this for another reason).
Basically i need to cast it to NText first and then XML. The only problem is my current format that works for selecting the Node value doesnt work to update it.
Error Message: Incorrect syntax near the keyword 'AS'.
UPDATE tbl_Module_RequestForms_Items
SET CAST(CAST(TicorOregon..tbl_Module_RequestForms_Items.XML AS NTEXT) AS XML).value('(//Record/Submitted)[1]', 'NVARCHAR(max)') = 'True'
WHERE CAST(CAST(TicorOregon..tbl_Module_RequestForms_Items.XML AS NTEXT) AS XML).value('(//Record/Submitted)[1]', 'NVARCHAR(max)') <> 'True'
XML Data:
<Record>
<Submitted>False</Submitted>
</Record>
There might be valid reason to store XML in a [n]varchar(max). If you want to only store the XML it is perfectly OK but if you want to modify parts of the XML using TSQL or if you need to query the XML for values or use node/attribute values in a where clause you should switch to XML where you can benefit from indexes on the data and skip the type conversions. Since text is deprecated you should at least consider to switch data type to [n]varchar(max)
If you had your data in a XML column you would use XML DML to modify the XML. In your case you would use replace value of like this.
update tbl_Module_RequestForms_Items
set XMLData.modify('replace value of (/Record/Submitted/text())[1] with "True"')
where XMLData.value('(/Record/Submitted)[1]', 'bit') = 0
Without the XML data type that is not possible so you have to extract the entire XML document, modify it and then update the table with the modified XML document.
You can of course do that using some kind of client development tool but it is also possible in TSQL.
Declare a table variable with the primary key from tbl_Module_RequestForms_Items and the XMLData column but as data type XML.
Copy the rows from tbl_Module_RequestForms_Items to the table variable that should be updated.
Update the XML using replace value of.
Apply the changes back to tbl_Module_RequestForms_Items.
Something like this where I assume that ID is the primary key for tbl_Module_RequestForms_Items and that your XML data is in column XMLData:
declare #T table
(
ID int primary key,
XMLData xml
)
insert into #T
select M.ID,
M.XMLData
from tbl_Module_RequestForms_Items as M
where cast(cast(XMLData as nvarchar(max)) as xml).value('(/Record/Submitted)[1]', 'bit') = 0
update #T
set XMLData.modify('replace value of (/Record/Submitted/text())[1] with "True"')
update M
set XMLData = cast(T.XMLData as nvarchar(max))
from tbl_Module_RequestForms_Items as M
inner join #T as T
on M.ID = T.ID
Not sure if this question makes for some poor performance down the track, but seems to at least feel "a better way" right now..
What I am trying to do is this:
I have a table called CONTACTS which amongst other things has a primary key field called memberID
I also have an XML field which contains the ID's of your friends (for example).. like:
<root><id>2</id><id>6</id><id>14</id></root>
So what I am trying to do via a stored proc is pass in say your member ID, and return all of your friends info, for example:
select name, address, age, dob from contacts
where id... xml join stuff...
The previous way I had it working (well sort of!) selected all the XML nodes (/root/id) into a temp table, and then did a join from that temp table to the contact table to get the contact fields...
Any help much appreciated.. just a bit overloaded from the .query .nodes examples, and of course which is maybe a better way of doing this...
THANKS IN ADVANCE!
<-- EDIT -->
I did get something working, but looks like a SQL frankenstein statement!
Basically I needed to get the friends contact ID's from the XML field, and populate into a temp table like so:
Declare #contactIDtable TABLE (ID int)
INSERT INTO #contactIDtable (ID)
SELECT CONVERT(INT,CAST(T2.memID.query('.') AS varchar(100))) AS friendsID
FROM dbo.members
CROSS APPLY memberContacts.nodes('/root/id/text()') AS T2(memID)
But crikey! the convert/cast thing looks serious.. as I need to get an INT for the next bit which is the actual join to return the contact data as follows:
SELECT memberID, memberName, memberAddress1
FROM members
INNER JOIN #contactIDtable cid
ON members.memberID = cid.ID
ORDER BY memberName
RESULT...
Well it works.. in my case, my memberContacts XML field had 3 nodes (id's in this case), and the above query returned 3 rows of data (memberID, memberName, memberAddress1)...
The whole point of this of course was to try to save creating a many join table i.e. list of all my friends ID's... just not sure if the above actually makes this quicker and easier...
Anymore ideas / more efficient ways of trying to do this???
SQL Server's syntax for reading XML is one of the least intuitive around. Ideally, you'd want to:
select f.name
from friends f
join #xml x
on x.id = f.id
Instead, SQL Server requires you to spell out everything. To turn an XML variable or column into a "rowset", you have to spell out the exact path and think up two aliases:
#xml.nodes('/root/id') as table_alias(column_alias)
Now you have to explain to SQL Server how to turn <id>1</id> into an int:
table_alias.column_alias.value('.', 'int')
So you can see why most people prefer to decode XML on the client side :)
A full example:
declare #friends table (id int, name varchar(50))
insert #friends (id, name)
select 2, 'Locke Lamorra'
union all select 6, 'Calo Sanzo'
union all select 10, 'Galdo Sanzo'
union all select 14, 'Jean Tannen'
declare #xml xml
set #xml = ' <root><id>2</id><id>6</id><id>14</id></root>'
select f.name
from #xml.nodes('/root/id') as table_alias(column_alias)
join #friends f
on table_alias.column_alias.value('.', 'int') = f.id
In order to get your XML contents as rows from a "pseudo-table", you need to use the .nodes() on the XML column - something like:
DECLARE #xmlfield XML
SET #xmlfield = '<root><id>2</id><id>6</id><id>14</id></root>'
SELECT
ROOT.ID.value('(.)[1]', 'int')
FROM
#xmfield.nodes('/root/id') AS ROOT(ID)
SELECT
(list of fields)
FROM
dbo.Contacts c
INNER JOIN
#xmlfield.nodes('/root/id') AS ROOT(ID) ON c.ID = Root.ID.value('(.)[1]', 'INT')
Basically, the .nodes() defines a pseudo-table ROOT with a single column ID, that will contain one row for each node in the XPath expression, and the .value() selects a specific value out of that XML fragment.