Get SQL Server column names with values - sql

I am trying to create a query that allows me to have the column name next to its column values that it finds.
As an example:
SELECT
*
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = 'saverTbl'
The above returns all of the column names in that table like this:
id, Link_userTblID, type, environment, vendor, theGUID, theGUID, etc etc...
Now what I need is to get the output from the table name next to the value. This is my query to just get my values from the table:
SELECT
*
FROM
saverTbl
WHERE
LINK_userTblID = #val1
The above returns the row that matches the LINK_userTblID like so:
32, 1, 'Blah', 'something', 'Adobe', 546656156-45332-54616516-4515, etc etc..
Now putting that all together (which is what this question is all about):
id: 32,
LINK_userTblID: 1,
type: Blah,
environment: something,
vendor: Adobe,
theGUID: 546656156-45332-54616516-4515,
etc etc.....
Pretty much needing the output in a json format but the column name matching up with the columns value.

Try this:
SELECT * FROM saverTbl
WHERE LINK_userTblID = #val1
FOR JSON PATH

Assuming, this is meant for one single row you can try this:
WITH cte AS
(
SELECT
(SELECT TOP 1 * FROM sys.objects FOR XML PATH('row'),TYPE) AS TheXml
)
SELECT TheElement.value('local-name(.)','nvarchar(max)')
+ ': '
+ TheElement.value('text()[1]','nvarchar(max)') AS YourOutput
FROM cte
CROSS APPLY TheXml.nodes('/row/*') AS A(TheElement);
The result:
YourOutput
---------------
name: sysrscols
object_id: 3
schema_id: 4
parent_object_id: 0
type: S
type_desc: SYSTEM_TABLE
create_date: 2012-02-10T20:15:58.693
modify_date: 2012-02-10T20:15:58.700
is_ms_shipped: 1
is_published: 0
is_schema_published: 0
XML in connection with XQuery and XPath is a very mighty toolset to solve rather generic problems. The cte builds an XML which looks like this:
<row>
<name>sysrscols</name>
<object_id>3</object_id>
<schema_id>4</schema_id>
<parent_object_id>0</parent_object_id>
<type>S </type>
<type_desc>SYSTEM_TABLE</type_desc>
<create_date>2012-02-10T20:15:58.693</create_date>
<modify_date>2012-02-10T20:15:58.700</modify_date>
<is_ms_shipped>1</is_ms_shipped>
<is_published>0</is_published>
<is_schema_published>0</is_schema_published>
</row>
The call to /row/* retrieves all nodes below <row> as derived table. The rest is rather easy XQuery.

Related

How to get a specific XML format from SQL server 2014 (example provided)

Ive been banging my head against a wall with this for ages - and i've finally caved.
Can any SQL / XML experts please take a look at the below and tell me the best way to get the required XML format using FOR XML (raw? / Auto? / Path?) please?
Thanks for your help
Test data
CREATE TABLE #CustomerData(
[Customer ID] int
, IsCustomerID bit DEFAULT 1
, Amount1 float DEFAULT 0
, Amount2 float DEFAULT 0
)
INSERT #CustomerData
SELECT *
FROM (
VALUES (12345, 1, 50, 75),
(12444, 1, 100, 100),
(12455, 1, 25, 65)
) zz ([Customer ID], IsCustomerID, Amount1, Amount2)
Ive tried various combinations of FOR XML RAW / FOR XML PATH and FOR XML AUTO, but nothing quite matches.
<Root>
<Customers>
<Customer Prefix="Loan">
<Property Name="Customer ID" Value="REF_1234" IsCustomerId="True" />
<Property Name="Amount1" Value="10" />
<Property Name="Amount2" Value="15" />
</Customer>
</Customers>
</Root>
"Loan" is a hardcoded text flag to be applied to all entries from the above table, although there is no specific column for it.
"Customer ID"
"IsCustomerID"
"Amount1"
"Amount2"
Are all fields in a single table
A few things:
You do not specify where the value of Prefix comes from in your result, but for the query structure this doesn't matter.
FOR XML PATH allows you to conjure nested elements and attributes with specific syntax ('#' for properties) and specifying TYPE to insert the subqueries as typed XML.
T-SQL cannot dynamically pivot columns to rows (only statically), so you can't (easily) write a query to produce every individual column in its own child element except by writing out the columns explicitly. This is only a problem if you really do need dynamic output (i.e. the query must continue to include all columns without rewrites even if people add columns later).
Because of the downright peculiar syntax T-SQL uses to insert FLOAT and REAL values into XML (always using scientific notation) it's usually a good idea to either not use FLOAT/REAL at all but specific integral types (with CONVERT), or use FORMAT to produce a friendlier value. Likewise, a BIT value will always be output as 1 or 0, so if you need other values you'll need to provide for that yourself.
So:
SELECT (
SELECT
"#Prefix" = 'Loan',
(SELECT
"#Name" = 'Customer ID',
"#Value" = [Customer ID],
"#IsCustomerID" = CASE IsCustomerId WHEN 1 THEN 'True' ELSE 'False' END
FOR XML PATH('Property'), TYPE
),
(SELECT "#Name" = 'Amount1', "#Value" = FORMAT(Amount1, 'G') FOR XML PATH('Property'), TYPE),
(SELECT "#Name" = 'Amount2', "#Value" = FORMAT(Amount2, 'G') FOR XML PATH('Property'), TYPE)
FROM #CustomerData
FOR XML PATH ('Customer'), ROOT('Customers'), TYPE
)
FOR XML PATH('Root')
It is possible to factor out the repeated mention of Property by using a subquery that uses UNION ALL:
SELECT (
SELECT
"#Prefix" = 'Loan',
(
SELECT * FROM (
SELECT
"#Name" = 'Customer ID',
"#Value" = [Customer ID],
"#IsCustomerID" = CASE IsCustomerId WHEN 1 THEN 'True' ELSE 'False' END
UNION ALL
SELECT "#Name" = 'Amount1', "#Value" = FORMAT(Amount1, 'G'), NULL
UNION ALL
SELECT "#Name" = 'Amount2', "#Value" = FORMAT(Amount2, 'G'), NULL
) _
FOR XML PATH('Property'), TYPE
)
FROM #CustomerData
FOR XML PATH ('Customer'), ROOT('Customers'), TYPE
)
FOR XML PATH('Root')
I can see either of these forms being more or less readable/maintainable depending on what's likely to change.

Querying XML colum for values

I have a SQL Server table with an XML column, and it contains data something like this:
<Query>
<QueryGroup>
<QueryRule>
<Attribute>Integration</Attribute>
<RuleOperator>8</RuleOperator>
<Value />
<Grouping>OrOperator</Grouping>
</QueryRule>
<QueryRule>
<Attribute>Integration</Attribute>
<RuleOperator>5</RuleOperator>
<Value>None</Value>
<Grouping>AndOperator</Grouping>
</QueryRule>
</QueryGroup>
</Query>
Each QueryRule will only have one Attribute, but each QueryGroup can have many QueryRules. Each Query can also have many QueryGroups.
I need to be able to pull all records that have one or more QueryRule with a certain attribute and value.
SELECT *
FROM QueryBuilderQueries
WHERE [the xml contains any value=X where the attribute is either Y or Z]
I've worked out how to check a specific QueryRule, but not "any".
SELECT
Query
FROM
QueryBuilderQueries
WHERE
Query.value('(/Query/QueryGroup/QueryRule/Value)[1]', 'varchar(max)') like 'UserToFind'
AND Query.value('(/Query/QueryGroup/QueryRule/Attribute)[1]', 'varchar(max)') in ('FirstName', 'LastName')
You can use two exist(). One to check the value and one to check Attribute.
select Q.Query
from dbo.QueryBuilderQueries as Q
where Q.Query.exist('/Query/QueryGroup/QueryRule/Value/text()[. = "UserToFind"]') = 1 and
Q.Query.exist('/Query/QueryGroup/QueryRule/Attribute/text()[. = ("FirstName", "LastName")]') = 1
If you really want the like equivalence when you search for a Value you can use contains().
select Q.Query
from dbo.QueryBuilderQueries as Q
where Q.Query.exist('/Query/QueryGroup/QueryRule/Value/text()[contains(., "UserToFind")]') = 1 and
Q.Query.exist('/Query/QueryGroup/QueryRule/Attribute/text()[. = ("FirstName", "LastName")]') = 1
According to http://technet.microsoft.com/pl-pl/library/ms178030%28v=sql.110%29.aspx
"The XQuery must return at most one value"
If you are quite certain that for example your XML has let's say maximum 10 QueryRules you could maybe use WHILE to loop everything while droping your results into temporary table?
maybe below can help you anyway
CREATE TABLE #temp(
Query type)
DECLARE #i INT
SET #i = 1
WHILE #i >= 10
BEGIN
INSERT INTO #temp
SELECT
Query
FROM QueryBuilderQueries
WHERE Query.value('(/Query/QueryGroup/QueryRule/Value)[#i]', 'varchar(max)') LIKE 'UserToFind'
AND Query.value('(/Query/QueryGroup/QueryRule/Attribute)[#i]', 'varchar(max)') IN ('FirstName', 'LastName')
#i = #i + 1
END
SELECT
*
FROM #temp
It's a pity that the SQL Server (I'm using 2008) does not support some XQuery functions related to string such as fn:matches, ... If it supported such functions, we could query right inside XQuery expression to determine if there is any. However we still have another approach. That is by turning all the possible values into the corresponding SQL row to use the WHERE and LIKE features of SQL for searching/filtering. After some experiementing with the nodes() method (used on an XML data), I think it's the best choice to go:
select *
from QueryBuilderQueries
where exists( select *
from Query.nodes('//QueryRule') as v(x)
where LOWER(v.x.value('(Attribute)[1]','varchar(max)'))
in ('firstname','lastname')
and v.x.value('(Value)[1]','varchar(max)') like 'UserToFind')

Get multiple xml nodes (delimited)

I have a table with a xml that is formatted something like this (simplified for readability)
<parentItem xmlns:i="http://tempuri.org/1" xmlns="http://tempuri.org/2">
<ItemA></ItemA>
<ItemB></ItemB>
<ItemC xmlns:d2p1="http://tempuri.org/3">
<d2p1:string>value1</d2p1:string>
<d2p1:string>value2</d2p1:string>
<d2p1:string>value3</d2p1:string>
<!-- .... (0 to many strings here) -->
</ItemC>
</parentItem>
The only think I care about are the values in parentItem > ItemC > string
I would like to get those values delimited by something, such as a comma
Desired Result: "value1,value2,value3"
currently I can get one value by doing this:
SELECT CAST([QueryXml] as xml).value('(/*:parentItem/*:ItemC/node())[1]','nvarchar(max)')
FROM [opendb].[dbo].[MyTable]
Result: "value1"
I can also get all the values like this:
SELECT CAST([QueryXml] as xml).value('(/*:ConflictsSearchTermQuery/*:TermItems)[1]','nvarchar(max)')
FROM [opendb].[dbo].[ConflictsSearchTerms]
Result: "value1value2value3"
but I'm looking to get a delimited set of values
Desired Result: "value1,value2,value3"
To get multiple values out of XML you need to use the nodes() method of the XML data type.
However, since this method does not return a single, scalar value (but a rowset), you need to call it through CROSS APPLY.
WITH MyTable AS (
SELECT 1 AS ID, CAST('<parentItem xmlns:i="http://tempuri.org/1" xmlns="http://tempuri.org/2">
<ItemA></ItemA>
<ItemB></ItemB>
<ItemC xmlns:d2p1="http://tempuri.org/3">
<d2p1:string>value1</d2p1:string>
<d2p1:string>value2</d2p1:string>
<d2p1:string>value3</d2p1:string>
<!-- .... (0 to many strings here) -->
</ItemC>
</parentItem>' AS XML) AS QueryXml
)
SELECT
t.ID,
x.node.value('.', 'varchar(100)') AS nodeValue
FROM
MyTable t
CROSS APPLY QueryXml.nodes('
declare namespace i="http://tempuri.org/1";
declare namespace def="http://tempuri.org/2";
declare namespace d2p1="http://tempuri.org/3";
/def:parentItem/def:ItemC/d2p1:string'
) x(node)
gives you
ID nodeValue
----------- ------------------
1 value1
1 value2
1 value3
After that, if you really must, standard techniques for concatenating values in SQL Server apply.
Note that I have properly declared the namespaces in the XQuery instead of using *. Namespaces are important, don't ignore them.

how to get all the fields in sql server 2008

I want to get all the fields from the table which i select in the query even if the fields contains no value(the value null or empty). the following is the query i have written.
SELECT (
SELECT T1.[CarrierCode_Destination] AS '#CarrierCode_Destination',
T1.[CarrierCode_Destination_Address] AS
'#CarrierCode_Destination_Address',
T1.[CarrierCode_Destination_Address1] AS
'#CarrierCode_Destination_Address1',
T1.[CarrierCode_Destination_Address2] AS
'#CarrierCode_Destination_Address2',
T1.[CIMtrek_RegContact] AS '#CIMtrek_RegContact',
T1.[CIMtrek_CarrierContact] AS '#CIMtrek_CarrierContact',
[T1].[CIMtrek_AdditionalContacts] AS
'#CIMtrek_AdditionalContacts'
FROM (
SELECT CD.[CIMtrek_DestinationName]
CarrierCode_Destination,
CD.[CIMtrek_DestinationAdd]
CarrierCode_Destination_Address,
CD.[CIMtrek_DestinationAdd_1]
CarrierCode_Destination_Address1,
CD.[CIMtrek_DestinationAdd_2]
CarrierCode_Destination_Address2,
CD.[CIMtrek_RegContact] CIMtrek_RegContact,
CD.[CIMtrek_CarrierContact] CIMtrek_CarrierContact,
CD.[CIMtrek_AdditionalContacts]
CIMtrek_AdditionalContacts
FROM CIMtrek_SystemTable_DatawareHouse CD
WHERE LEN(
LTRIM(
RTRIM(ISNULL(LTRIM(RTRIM(CD.[CIMtrek_DestinationName])), ''))
)
) != 0
) AS T1
FOR XML PATH('Record'),
TYPE
) FOR XML PATH('CarrierCode_Destination'),
TYPE
the results i get is
<CarrierCode_Destination>
<Record CarrierCode_Destination="8S - San Fran" CarrierCode_Destination_Address="SAN FRANCISCO - Redistribution" CarrierCode_Destination_Address1="4025 Whipple Road, " CarrierCode_Destination_Address2="Union City CA 94587" CIMtrek_RegContact="RDC San Francisco" />
<Record CarrierCode_Destination="8G - St Louis" CarrierCode_Destination_Address="ST. LOUIS - Redistribution" CarrierCode_Destination_Address1="126 Enterprise Drive, " CarrierCode_Destination_Address2="Wentzville MO 63385" CIMtrek_RegContact="RDC St Louis" />
<Record CarrierCode_Destination="V8 PHO/CYPR/CUST/TL  " />
</CarrierCode_Destination>
but i want all the fields which are selected in the query. If you see the result it gives me the fields which are having value and omits the fields which don't have values.
how to do this, Please help.
Best Regards
If you use isnull() statements for each column you are selecting and convert null values to empty strings it should force the attributes to be generated for all columns. You may or may not also need to add rtrim() statements like I show below depending on if you have a datatype that is going to return a fixed size string:
SELECT (
SELECT rtrim(isnull(T1.[CarrierCode_Destination],'')) AS '#CarrierCode_Destination',
rtrim(isnull(T1.[CarrierCode_Destination_Address],'')) AS
'#CarrierCode_Destination_Address',
rtrim(IsNull(T1.[CarrierCode_Destination_Address1],'')) AS
'#CarrierCode_Destination_Address1',
rtrim(IsNull(T1.[CarrierCode_Destination_Address2],'')) AS
'#CarrierCode_Destination_Address2',
rtrim(IsNull(T1.[CIMtrek_RegContact],'')) AS '#CIMtrek_RegContact',
rtrim(IsNull(T1.[CIMtrek_CarrierContact],'')) AS '#CIMtrek_CarrierContact',
rtrim(IsNull([T1].[CIMtrek_AdditionalContacts],'')) AS
'#CIMtrek_AdditionalContacts'
FROM (
SELECT CD.[CIMtrek_DestinationName]
CarrierCode_Destination,
CD.[CIMtrek_DestinationAdd]
CarrierCode_Destination_Address,
CD.[CIMtrek_DestinationAdd_1]
CarrierCode_Destination_Address1,
CD.[CIMtrek_DestinationAdd_2]
CarrierCode_Destination_Address2,
CD.[CIMtrek_RegContact] CIMtrek_RegContact,
CD.[CIMtrek_CarrierContact] CIMtrek_CarrierContact,
CD.[CIMtrek_AdditionalContacts]
CIMtrek_AdditionalContacts
FROM CIMtrek_SystemTable_DatawareHouse CD
WHERE LEN(
LTRIM(
RTRIM(ISNULL(LTRIM(RTRIM(CD.[CIMtrek_DestinationName])), ''))
)
) != 0
) AS T1
FOR XML PATH('Record'),
TYPE
) FOR XML PATH('CarrierCode_Destination'),
TYPE
That worked for me when I tested it out but if you are also having issues with empty values not generating attributes you can first force all the empty values to be returned as NULL from the select against your original data table using NULLIF()
SELECT (
SELECT rtrim(isnull(T1.[CarrierCode_Destination],'')) AS '#CarrierCode_Destination',
rtrim(isnull(T1.[CarrierCode_Destination_Address],'')) AS
'#CarrierCode_Destination_Address',
rtrim(IsNull(T1.[CarrierCode_Destination_Address1],'')) AS
'#CarrierCode_Destination_Address1',
rtrim(IsNull(T1.[CarrierCode_Destination_Address2],'')) AS
'#CarrierCode_Destination_Address2',
rtrim(IsNull(T1.[CIMtrek_RegContact],'')) AS '#CIMtrek_RegContact',
rtrim(IsNull(T1.[CIMtrek_CarrierContact],'')) AS '#CIMtrek_CarrierContact',
rtrim(IsNull([T1].[CIMtrek_AdditionalContacts],'')) AS
'#CIMtrek_AdditionalContacts'
FROM (
SELECT NullIF(CD.[CIMtrek_DestinationName],'')
CarrierCode_Destination,
NullIF(CD.[CIMtrek_DestinationAdd],'')
CarrierCode_Destination_Address,
NullIF(CD.[CIMtrek_DestinationAdd_1],'')
CarrierCode_Destination_Address1,
NullIF(CD.[CIMtrek_DestinationAdd_2],'')
CarrierCode_Destination_Address2,
NullIF(CD.[CIMtrek_RegContact],'') CIMtrek_RegContact,
NullIF(CD.[CIMtrek_CarrierContact],'') CIMtrek_CarrierContact,
NullIF(CD.[CIMtrek_AdditionalContacts],'')
CIMtrek_AdditionalContacts
FROM CIMtrek_SystemTable_DatawareHouse CD
WHERE LEN(
LTRIM(
RTRIM(ISNULL(LTRIM(RTRIM(CD.[CIMtrek_DestinationName])), ''))
)
) != 0
) AS T1
FOR XML PATH('Record'),
TYPE
) FOR XML PATH('CarrierCode_Destination'),
TYPE
I normally use the following syntax:
SELECT *
FROM TABLE_NAME
WHERE FIELD_NAME = "CRITERIA"

Check if a value exists in a collection stored in XML data type column

I have an XML data type column called "tags".
In that, I am storing a collection, like so:
<ArrayOfString>
<string>personal</string>
<string>travel</string>
<string>gadgets</string>
<string>parenting</string>
</ArrayOfString>
I want to select all the rows, that have one of the values that I am looking for: for example, I want to select all rows in the table that have a tag "travel".
I know that this works, if I know the index of the value I am looking for:
select * from posts
where tags.value('(/ArrayOfString/string)[1]', 'nvarchar(1000)') = 'travel'
but this query works only if the tag "travel" is the 2nd item in the nodes. How do I check if a value exists, irrespective of the position it is in?
select *
from tags
where tags.exist('/ArrayOfString/string[. = "travel"]') = 1
Or like this if you want to check against a variable.
declare #Val varchar(10)
set #Val = 'travel'
select *
from tags
where tags.exist('/ArrayOfString/string[. = sql:variable("#Val")]') = 1
You can try something like this:
SELECT
*
FROM
dbo.Posts
WHERE
tags.exist('/ArrayOfString/string/text()[. = "travel"]') = 1
This will list all the rows that have "travel" in one of the strings in your XML