I have a search field in an admin system where a user can enter search criteria to find a product to edit within their shop. Some of the product names are long and similar. For example I have multiple product names with measurements in them. Here is an example:
Widget Brown Bull Edge Stone 400x500x600
Widget Brown Bull Snow Stone 700x500x300
There are about a hundred products like this, similar in name with only slight variations in either colour, size or description.
I've tried using regEx without much luck, I've also tried variations of LIKE without luck either unfortunately.
I want to be able to enter search criteria in my HTML form field and use SQL to limit the results based on the amount of search criteria I enter.
For example if I entered:
"Widget Brown 400" in the search field I can't use %like% in the where clause as it won't match that pattern.
My ideal result would be for me to enter Widget Brown 400 and the list of results would show those products that match that search criteria.
As commnented by Gordon Linoff, the best approach would be to settle for SQL-Server full text search.
Apart from that, another, less elegant and performant solution would be to use table valued function STRING_SPLIT() (available in SQL Server 2016), to turn the search words to a derived table, and then search each individual word in the text using LIKE. If all searched words are present in the text, then you have a match.
Consider:
SELECT *
FROM mytable t
WHERE NOT EXISTS (
SELECT 1
FROM STRING_SPLIT(#search, ' ') x
WHERE t.txt NOT LIKE '%' + x.value + '%'
);
Demo on DB Fiddle
CREATE TABLE mytable (id int, txt varchar(max));
INSERT INTO mytable VALUES
(1, 'Widget Brown Bull Edge Stone 400x500x600'),
(2, 'Widget Brown Bull Snow Stone 700x500x300');
DECLARE #search NVARCHAR(400) = 'Widget Brown 400';
SELECT *
FROM mytable t
WHERE NOT EXISTS (
SELECT 1
FROM STRING_SPLIT(#search, ' ') x
WHERE t.txt NOT LIKE '%' + x.value + '%'
);
id | txt
-: | :---------------------------------------
1 | Widget Brown Bull Edge Stone 400x500x600
Related
I have a BigQuery table that looks like this, where response_group is a string
id
response_group
A1
[{"Answer":"blue","Question":"what's your favourite colour?"},{"Answer":"dogs","Question":"do you prefer dogs or cats?"},{"Answer":"coffee", "Question":"do you prefer tea or coffee?"}]
A2
[{"Answer":"green","Question":"what's your favourite colour?"},{"Answer":"Superman","Question":"who's your favourite superhero?"},{"Answer":"coffee", "Question":"do you prefer tea or coffee?"}]
As shown here, not all of the questions are the same for each ID.
What I'd like to create is a table like this
id
what's your favourite colour?
do you prefer dogs or cats?
do you prefer tea or coffee?
who's your favourite superhero?
A1
blue
dogs
coffee
A2
green
coffee
superman
I've made lots of attempts at a solution, but can't get anything to work.
Can anyone point me in the right direction?
Try below
create temp table tmp as (
select id,
translate(json_value(response, '$.Question'), " '?", "_") question,
json_value(response, '$.Answer') answer
from your_table, unnest(json_extract_array(response_group)) response
);
execute immediate (
select 'select * from tmp pivot (any_value(answer) for question in (' ||
string_agg(distinct '"' || question || '"') || '))'
from tmp
);
if applied to sample data in your question - output is
To test above I was using dummy data from your question
create temp table your_table as (
select 'A1' id, '''[{"Answer":"blue","Question":"what's your favourite colour?"},{"Answer":"dogs","Question":"do you prefer dogs or cats?"},{"Answer":"coffee", "Question":"do you prefer tea or coffee?"}]''' response_group union all
select 'A2', '''[{"Answer":"green","Question":"what's your favourite colour?"},{"Answer":"Superman","Question":"who's your favourite superhero?"},{"Answer":"coffee", "Question":"do you prefer tea or coffee?"}]'''
);
I'm trying to create a simple search page on my site but finding it difficult to get full text search working as I would expect it to word.
Example search:
select *
from Movies
where contains(Name, '"Movie*" and "2*"')
Example data:
Id Name
------------------
1 MyMovie
2 MyMovie Part 2
3 MyMovie Part 3
4 MyMovie X2
5 Other Movie
Searches like "movie*" return no results since the term is in the middle of a work.
Searches like "MyMovie*" and "2*" only return MyMovie Part 2 and not MyMovie Part X2
It seems like I could just hack together a dynamic SQL query that will just
and name like '%movie%' and name like '%x2%' and it would work better than full text search which seems odd since it's a large part of SQL but doesn't seem to work as good as a simple like usage.
I've turned off my stop list so the number and single character results appear but it just doesn't seem to work well for what I'm doing which seems rather basic.
select
*
from Movies
where
name like ('%Movie%')
or name like ('%2%')
;
select * from Movies where freetext(Name, ' "Movie*" or "2*" ')
Let's say i have the following table
user|text
1 |red 123 orange blue green
2 |red orange blue
3 |blue orange 123 red
If I wanted to pull all users whose text includes both '123' and 'blue', how would i do it? I would want to pull user 1 and 3.
SELECT *
FROM Table
WHERE text LIKE '%123%'&&'%blue%'
OR text LIKE '%blue%'&&'%123%'
Is this better solved thru using a regexp function?
Try this code:
SELECT *
FROM Table
WHERE text LIKE '%123%'
and text like '%blue%'
You need to repeat LIKE for each pattern.
SELECT *
FROM Table
WHERE text LIKE '%123%' AND text LIKE '%blue%'
You could also write it as:
WHERE text LIKE '%123%blue%' OR text LIKE '%blue%123%'
or:
WHERE text RLIKE '123.*blue|blue.*123'
However, these two solutions get exponentially large if you have to match several strings in any order. The first version is linear in the number of matches.
I've searched high and low for the answer to this, but I can't figure it out. I'm relatively new to SQL Server and don't quite have the syntax down yet. I have this datastructure (simplified):
Table "Users" | Table "Tags":
UserID UserName | TagID UserID PhotoID
1 Bob | 1 1 1
2 Bill | 2 2 1
3 Jane | 3 3 1
4 Sam | 4 2 2
-----------------------------------------------------
Table "Photos": | Table "Albums":
PhotoID UserID AlbumID | AlbumID UserID
1 1 1 | 1 1
2 1 1 | 2 3
3 1 1 | 3 2
4 3 2 |
5 3 2 |
I'm looking for a way to get the all the photo info (easy) plus all the tags for that photo concatenated like CONCAT(username, ', ') AS Tags of course with the last comma removed. I'm having a bear of a time trying to do this. I've tried the method in this article but I get an error when I try to run the query saying that I can't use DECLARE statements... do you guys have any idea how this can be done? I'm using VS08 and whatever DB is installed in it (I normally use MySQL so I don't know what flavor of DB this really is... it's an .mdf file?)
Ok, I feel like I need to jump in to comment about How do you concat multiple rows into one column in SQL Server? and provide a more preferred answer.
I'm really sorry, but using scalar-valued functions like this will kill performance. Just open SQL Profiler and have a look at what's going on when you use a scalar-function that calls a table.
Also, the "update a variable" technique for concatenation is not encouraged, as that functionality might not continue in future versions.
The preferred way of doing string concatenation to use FOR XML PATH instead.
select
stuff((select ', ' + t.tag from tags t where t.photoid = p.photoid order by tag for xml path('')),1,2,'') as taglist
,*
from photos
order by photoid;
For examples of how FOR XML PATH works, consider the following, imagining that you have a table with two fields called 'id' and 'name'
SELECT id, name
FROM table
order by name
FOR XML PATH('item'),root('itemlist')
;
Gives:
<itemlist><item><id>2</id><name>Aardvark</a></item><item><id>1</id><name>Zebra</name></item></itemlist>
But if you leave out the ROOT, you get something slightly different:
SELECT id, name
FROM table
order by name
FOR XML PATH('item')
;
<item><id>2</id><name>Aardvark</a></item><item><id>1</id><name>Zebra</name></item>
And if you put an empty PATH string, you get even closer to ordinary string concatenation:
SELECT id, name
FROM table
order by name
FOR XML PATH('')
;
<id>2</id><name>Aardvark</a><id>1</id><name>Zebra</name>
Now comes the really tricky bit... If you name a column starting with an # sign, it becomes an attribute, and if a column doesn't have a name (or you call it [*]), then it leaves out that tag too:
SELECT ',' + name
FROM table
order by name
FOR XML PATH('')
;
,Aardvark,Zebra
Now finally, to strip the leading comma, the STUFF command comes in. STUFF(s,x,n,s2) pulls out n characters of s, starting at position x. In their place, it puts s2. So:
SELECT STUFF('abcde',2,3,'123456');
gives:
a123456e
So now have a look at my query above for your taglist.
select
stuff((select ', ' + t.tag from tags t where t.photoid = p.photoid order by tag for xml path('')),1,2,'') as taglist
,*
from photos
order by photoid;
For each photo, I have a subquery which grabs the tags and concatenates them (in order) with a commma and a space. Then I surround that subquery in a stuff command to strip the leading comma and space.
I apologise for any typos - I haven't actually created the tables on my own machine to test this.
Rob
I'd create a UDF:
create function GetTags(PhotoID int) returns #tags varchar(max)
as
begin
declare #mytags varchar(max)
set #mytags = ''
select #mytags = #mytags + ', ' + tag from tags where photoid = #photoid
return substring(#mytags, 3, 8000)
end
Then, all you have to do is:
select GetTags(photoID) as tagList from photos
Street_Name ; Street_Code
west | 14
east | 7
west+east | 714
If want to show two different row concat itself , how can do it?
(I mean last row i want to show from select result. My table had first and secord record)
I have two tables, Book and Tag, and books are tagged using the association table BookTag. I want to create a report that contains a list of books, and for each book a list of the book's tags. Tag IDs will suffice, tag names are not necessary.
Example:
Book table:
Book ID | Book Name
28 | Dracula
BookTag table:
Book ID | Tag ID
28 | 101
28 | 102
In my report, I'd like to show that book #28 has the tags 101 and 102:
Book ID | Book Name | Tags
28 | Dracula | 101, 102
Is there a way to do this in-line, without having to resort to functions or stored procedures? I am using SQL Server 2005.
Please note that the same question already has been asked in Combine multiple results in a subquery into a single comma-separated value, but the solution involves creating a function. I am asking if there is a way to solve this without having to create a function or a stored procedure.
You can almost do it. The only problem I haven't resolved is the comma delimiter. Here is a query on a similar structure that separates the tags using a space.
SELECT em.Code,
(SELECT et.Name + ' ' AS 'data()'
FROM tblEmployeeTag et
JOIN tblEmployeeTagAssignment eta ON et.Id = eta.EmployeeTag_Id AND eta.Employee_Id = em.id
FOR XML PATH('') ) AS Tags
FROM tblEmployee em
Edit:
Here is the complete version using your tables and using a comma delimiter:
SELECT bk.Id AS BookId,
bk.Name AS BookName,
REPLACE((SELECT LTRIM(STR(bt.TagId)) + ', ' AS 'data()'
FROM BookTag bt
WHERE bt.BookId = bk.Id
FOR XML PATH('') ) + 'x', ', x','') AS Tags
FROM Book bk
I suppose for future reference I should explain a bit about what is going on. The 'data()' column name is a special value that is related to the FOR XML PATH statement. It causes the XML document to be rendered as if you did an .InnerText on the root node of the resulting XML.
The REPLACE statement is a trick to remove the trailing comma. By appending a unique character (I randomly chose 'x') to the end of the tag list I can search for comma-space-character and replace it with an empty string. That allows me to chop off just the last comma. This assumes that you are never going to have that sequence of characters in your tags.
Unless you know what the tag ids/names are and can hard code them into your query, I'm afraid the answer is no.
If you knew the maximum number of tags for a book, you could use a pivot to get them to the same row and then use COALESCE, but in general, I don't believe there is.
The cleanest solution is probably to use a custom C# CLR aggregate function. We have found that this works really well. You can find instructions for creating this at http://dotnet-enthusiast.blogspot.com/2007/05/user-defined-aggregate-function-in-sql.html