Query a table to find records with given text - sql

I have below 3 records in my table(TAG_DATA) column(TAGS).
car,bus,van
bus,car,ship,van
ship
I wrote a query to get records which has car and bus as below.
SELECT * FROM TAG_DATA
where TAGS like '%car, bus%'
But above query return only below record.
car,bus,van
But i need to get output as below. because both records have car and bus
car,bus,van
bus,car,ship,van
How can i write a query for this ? I'm using MS SQL Server.
UPDATED
I'm selecting tags from multi select combobox in my application. so i need to give text in that. so can't use and/ or in my query.

Please try:
DECLARE #input NVARCHAR(MAX)='car, bus'
SELECT DISTINCT B.*
FROM(
SELECT
LTRIM(Split.a.value('.', 'VARCHAR(100)')) AS CVS
FROM
(
SELECT
CAST ('<M>' + REPLACE(#input, ',', '</M><M>') + '</M>' AS XML) AS CVS
) AS A CROSS APPLY CVS.nodes ('/M') AS Split(a)
)x INNER JOIN TAG_DATA b on TAGS like '%'+CVS+'%'

Here, only records having car, bus tags in sequence will be fetched.
For the desired result, below query is beneficial :
SELECT * FROM TAG_DATA
where TAGS like '%car, bus%' or TAGS like '%bus, car%'
SQL FIDDLE

SELECT * FROM TAG_DATA
where TAGS like '%car,%' and TAGS like '%bus,%'
I intentionally used the commas. It depends on your data The following query will also work atleast for the example above.
SELECT * FROM TAG_DATA
where TAGS like '%car,%' and TAGS like '%bus,%'

This works
SELECT * FROM TAG_DATA
where TAGS like '%[BUS,CAR],[CAR,BUS]%'

Related

SQL Server: display whole column only if substring found

Working with SQL Sever 2016. I am constrained by the fact we cannot create functions or stored procedures. I am trying to find %word% in many columns across a table (75). Right now, I have a very large clump of
and (fieldname1 like %word%
or fieldname2 like %word%
or fieldname3 like %word%) etc.
While cumbersome, this does provide me the correct results. However:
I am looking to simplify this and
in the select, I want to display the whole column if and only if it finds %word% (or even just the column name would work)
Thank you in advance for any thoughts.
--...slow...
declare #searchfor varchar(100) = '23';
select #searchfor as [thevalue],
thexml.query('for $a in (/*[contains(upper-case(.), upper-case(sql:variable("#searchfor")))])
return concat(local-name($a[1]), ",")').value('.', 'nvarchar(max)') as [appears_in_columns],
*
from
(
select *, (select o.* for xml path(''), type) as thexml
from sys.all_objects as o --table goes here
) as src
where thexml.exist('/*[contains(upper-case(.), upper-case(sql:variable("#searchfor")))]') = 1;
One option uses cross apply to unpivot the table and then search:
select v.*
from mytable t
cross apply (values
('fieldname1', fieldname1),
('fieldname2', fieldname2),
('fieldname3', fieldname3)
) v(fieldname, fieldvalue)
where v.fieldvalue like '%word%'
Note that if more than one column contains the search word, you will get several rows in the resultset. I am unsure how you want to handle this use case (there are options).
SELECT OBJECT_NAME(id) ObjectName , [Text]
FROM syscomments
WHERE TEXT LIKE '%word%'

Alternative for a WITH inside FROM

I`m trying to make a query in ssms that shows all errors and the amount of them.
The error message is in XML but in a nvarchar field, thats why I have to do a "with".
;with CastToXML as (
select CAST(tableName.XMLField as xml) as x
from tableName
group by tableName.XMLField
)
select distinct h.ep.value('(./logMessage)[1]', 'VARCHAR(max)') as log1
from CastToXML
cross apply x.nodes('xmlfield') as h(ep)
This doesn't allow me to do a group by log1.
Please help me.
If you want to add a COUNT(*) to your final results, just move your current query into another CTE and query and group by its results (after removing the DISTINCT, obviously):
;with CastToXML as (
select CAST(tableName.XMLField as xml) as x
from tableName
group by tableName.XMLField
), Errors as (
select h.ep.value('(./logMessage)[1]', 'VARCHAR(max)') as log1
from CastToXML
cross apply
x.nodes('xmlfield') as h(ep)
)
select log1,COUNT(*)
from Errors
group by log1
If I get this correctly, you want to count the messages within your XML.
Besides the fact, that you really should store your XML in a natively typed column (the repeated cast to XML is very expensive!), you can use simple XQuery to achieve the same:
DECLARE #SomeXML XML=
N'<xmlfield>
<logMessage>Message 1</logMessage>
<logMessage>Message 2</logMessage>
<logMessage>Message 3</logMessage>
<logMessage>Message 1</logMessage>
</xmlfield>';
SELECT #SomeXML.value('count(/xmlfield/logMessage)','int') AS CountOfLogMessages
,#SomeXML.value('count(distinct-values(/xmlfield/logMessage))','int') AS DistinctCountOfLogMessages
Hint You can use this in your query in the same place as you've got the other .value() call.

Shred XML For Each Row in SQL Table

I have a table that contains two columns, and ID, and XML data. I'd like to shred the XML for each ID. I'm pulling out a single value within the XML and all the XML is structured the same I'm just not sure how to loop through the table and apply XML query to each row.
The query I need to apply is as follows:
Select top 1
Element1 = XV.value('(.)[1]','nvarchar(32)')
from #xml.nodes('Parameters/Parameter/Value') as x(XV)
So the end results would have two columns, ID and shredded value from XML.
Without any knowledge about your actual XML and how you want to shred it to get some values it is impossible to answer in completness, but this shoudl point you in the right direction:
Returns the ID and the XML as is
SELECT ID
,TheXmlColumn
FROM YourTable
This returns the ID and a value out of your XML
SELECT ID
,TheXmlColumn.value('Some XPaht','SomeType') AS SomeValueFromXML
FROM YourTable
And if there are more embedded rows it would be something like this
SELECT ID
,nd.value('Some XPaht','SomeType') AS SomeValueFromXMLRow
FROM YourTable
OUTER APPLY TheXmlColumn.nodes('SomeXPath') AS A(nd)
My magic glass bulb tells me, that you might need something like this:
SELECT ID
,TheXmlColumn.value('(Parameters/Parameter/Value)[1]','nvarchar(max)') AS SomeValueFromXML
FROM YourTable

Count the occurences of all individual values in a multivalued field in SQL Server

Features Impressive
A,B,C
D,C
A,D
B,C,D
This is a column in my database that contains multiple values that comes from combobox.
I want to count the number of occurrences of each value in this column so that I can generate a bar chart out of this reflecting how many people liked the specific features.
Output I want is
A- 2
B- 2
C- 3
D- 3
Please help me with this SQL query.
You have a very poor design. You should be storing individual values in a separate row in a junction table -- one row per whatever and value.
Given the data structure, here is a method to do what you want -- assuming that you have a lit of allowed values:
select av.feature, count(t.feature)
from AllowedValues av left join
tables t
on ',' + av.feature + ',' like '%,' + t.features + ',%'
group by av.feature;
If you don't have an explicit list of features, you can create one using a CTE, something like:
with AllowedValues as
select 'A' as feature union all
. . .
)
The performance of this query will be lousy. And, there is really no way to make it better without fixing the data structure.
So, I repeat. You should fix the data structure and use a junction table instead of storing a list as a string. In SQL, tables are for storing lists. Strings are for, well, storing strings.
As mentioned by others really this is poor design you should never store comma separated values in a single column.
Use a Split Function to split the comma separated values into individual rows then count the individual rows. Something like this.
;With CTE as
(
SELECT Split.a.value('.', 'VARCHAR(100)') SP_COL
FROM (SELECT Cast ('<M>' + Replace(feature, ',', '</M><M>') + '</M>' AS XML) AS Data
FROM [table]) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
)
Select SP_COL,COUNT(1) as [COUNT]
FROM CTE
Group By SP_COL

Creating a lookup table from a column in table

I have a table in SQL Server that contains an ID and also a column with multiple values separated by a comma (like the example below)
ID Category_number
-------------------------
1 3,5,6,8
2 4,8,23
3 4,7,5,3
I need to make this into a lookup table with 1 category number per row, so like below;
ID Category_Number
-------------------------
1 3
1 5
1 6
I have been told that XPATH might be the solution to this. Does anyone have any sample code that will do this?
Thanks
See this answer Split
Create that function in your database.
Then you can create the results you want using:
SELECT
ID,
A.S Category_Number
FROM
MyCsvTable
CROSS APPLY dbo.Split (',', MyCsvTable.Category_Number) A
I know you just accepted the solution, but assuming you're using SQL Server, here is an alternative approach without building a function:
SELECT A.[id],
Split.a.value('.', 'VARCHAR(100)') AS Cat
FROM
(SELECT [id],
CAST ('<M>' + REPLACE(Cat, ',', '</M><M>') + '</M>' AS XML) AS String
FROM YourTable
) AS A
CROSS APPLY String.nodes ('/M') AS Split(a)
And some Fiddle: http://sqlfiddle.com/#!3/cf427/3
Best of luck!
You may also want to check HierarchyID in SQL SERVER. Some tutorial: http://www.codeproject.com/Articles/37171/HierarchyID-Data-Type-in-SQL-Server-2008
Basically all you need to do is to loop through string and find position of commas and replace them with chr(13) or such. In Oracle it can be done easily in a single query. I think the same can be done using HierarchyID in SQL Server starting from 2008 or maybe even earlier versions.