Search parameter in stored procedure not functioning - sql-server-2008

I'm trying to create a stored procedure in SQL Server 2008 that searches the (Chinook, if it helps) database's Track table for tracks that match a (part of) an album title. For example, when the parameter is "rock", I'd like to see all the tracks that are on albums that have titles with "rock" in them someplace.
Hard-coding 'rock' works as expected, but running the procedure from Management Studio with 'rock' as the parameter returns a lot of non-"rock" results.
Here is the script I am using to tinker with my stored procedure:
ALTER PROCEDURE [dbo].[searchTrackAlbumTitle]
#trackAlbumTitle nvarchar
AS
BEGIN
SET NOCOUNT ON;
SELECT Track.TrackId
,Track.Name AS TrackName
,Artist.Name AS ArtistName
,Album.Title AS AlbumTitle
,Track.Composer
,Track.Milliseconds
,Track.Bytes
,MediaType.Name AS TypeName
,Genre.Name AS GenreName
,Track.UnitPrice
FROM Track
LEFT JOIN Album ON Track.AlbumId = Album.AlbumId
LEFT JOIN Artist ON Album.ArtistId = Artist.ArtistId
LEFT JOIN MediaType ON Track.MediaTypeId = MediaType.MediaTypeId
LEFT JOIN Genre ON Track.GenreId = Genre.GenreId
WHERE Album.Title LIKE ( '%' + #trackAlbumTitle + '%' ) -- doesn't work
--WHERE Album.Title LIKE ( '%' + 'rock' + '%' ) -- works
END

Put a length on your parameter. e.g.
#trackAlbumTitle NVARCHAR(255)
Right now it's being truncated to NVARCHAR(1). See this blog post for more info - it applies to NVARCHAR, too.
Also note that if you say WHERE Album.anything the LEFT JOIN becomes an INNER JOIN. So you should either move it to the ON clause or change the join to an explicit INNER JOIN. Right now it's confusing - I don't know which behavior you want (and they are different).

try this:
WHERE Album.Title LIKE ( '%' + rtrim(#trackAlbumTitle) + '%' )

Related

How could I make a fullname search work with a seperate last and firstname?

I'm new to SQL and I'm trying to make a basic query work.
This is the table and it's layout that I'm trying to search against:
https://prnt.sc/20wj54g
What I'm aiming to do is have a search term input, then have the input be used to search for names in the database by fullname.
This is the current query I have now:
SELECT *
FROM personnel p
LEFT JOIN department d ON (d.id = p.departmentID)
LEFT JOIN location l ON (l.id = d.locationID)
WHERE p.firstName LIKE '%Robert Heffron%' OR p.lastName LIKE '%Robert Heffron%';
This doesn't work as there is no last or firstname which contains the whole string "Robert Heffron" however this means if the user typed in that string looking for that person by the full name they wouldn't find them.
I'm currently using PHP and JS to display the data but I'm struggling with the SQL part.
If anyone could help I'd be very grateful.
This is not a good solution for performance, but if you want to solve the problem only using SQL, you can try this:
SELECT *
FROM personnel p
LEFT JOIN department d ON (d.id = p.departmentID)
LEFT JOIN location l ON (l.id = d.locationID)
WHERE LOWER(CONCAT(p.firstName, p.lastName)) LIKE LOWER(REPLACE('%Robert Heffron%', ' ', '%'))
OR LOWER(CONCAT(p.lastName, p.firstName)) LIKE LOWER(REPLACE('%Robert Heffron%', ' ', '%'));
Field concatenation is used to find a concatenated input value, and different combinations of OR concatenation are used to be independent of the order in which the first and last name are entered. Convert to lowercase - for case insensitive searches. We replace spaces with a % character to search for concatenated field values without worrying about spaces.
I would suggest adding a full name field to your database table and whenever you create a "personnel" in your code, you concatenate the first and last name:
Fullname = Firstname + " " + Lastname
if you don't like this, maybe use a string split function (in php it might be .explode()) and then adding WHERE OR statements for each substring for first and last name.
Use the following combination to find people whose name is Robert and whose last name is Heffron
Just find Robert Heffron
WHERE p.firstName LIKE '%Robert%' AND p.lastName LIKE '%Heffron%';
find all first names that have Robert and all last names that have Heffron
WHERE p.firstName LIKE '%Robert%' OR p.lastName LIKE '%Heffron%';
A solution that wouldn't ask you to change the table and having only one input variable could be something like this:
DECLARE #FullName AS VARCHAR(100)
SET #FullName = 'Robert Heffron'
SET #FullName = REPLACE(#FullName, ' ', '.')
SELECT....
WHERE p.firstname LIKE '%'+ ParseName(#FullName, 2) + '%' OR
p.firstname LIKE '%'+ ParseName(#FullName, 1) + '%' OR
p.lastnameLIKE '%'+ ParseName(#FullName, 2) + '%' OR
p.lastname LIKE '%'+ ParseName(#FullName, 1) + '%'
Downside in my opinion is if the user enters weird amount of spaces in the name. I have tested replacing the array number 1 or 2 in the ParseName with a number which was impossible, like 7, and it basically just ignores it.. no errors thrown or whatever. If you use this, you could test it on your side just to make sure tho.
You can use concat for the columns and can use "HAVING" clause on the concatenated column. I hope it will work for you.
SELECT * FROM personnel p LEFT JOIN department d ON (d.id = p.departmentID) LEFT JOIN location l ON (l.id = d.locationID) HAVING CONCAT(firstName,' ',lastName) LIKE '%Robert Heffron%';
I hope this snippet will work for you.

JOIN on keys that don't have the same value

I am trying to do an INNER JOIN on two tables that have similar values, but not quite the same. One table has a fully qualified host name for its primary key, and the other the hosts short name, as well as the subdomain. It it safe to assume that the short name and the subdomain together are unique.
So I've tried:
SELECT table1.nisinfo.* FROM table1.nisinfo INNER JOIN table2.hosts ON (table1.nisinfo.shortname + '.' + table1.nisinfo.subdomainname + '.domain.com') = table2.hosts.fqhn WHERE table2.hosts.package = 'somepkg';
This doesn't return the results I expect, it returns the first result hundreds of times. I'd like to return distinct rows. It takes a long time to run as well.
What am I doing wrong? I was thinking of running a subquery to get the hostnames, but I don't know what the right path from here is.
Thank you!
You can use group by in your query so you can achieve the desired results you want
please see this two links
Group by with 2 distinct columns in SQL Server
http://www.sqlteam.com/article/how-to-use-group-by-with-distinct-aggregates-and-derived-tables
Try putting your results into a temp table and then view the table to make sure that the columns are as expected.
SELECT table1.nisinfo.*, table1.nisinfo.shortname + '.' + table1.nisinfo.subdomainname + '.domain.com' AS ColID
INTO #temp
FROM table1.nisinfo;
Select *
from #temp INNER JOIN table2.hosts ON ##temp.ColID = table2.hosts.fqhn
WHERE table2.hosts.package = 'somepkg'
;
Put a Group By clause at the end of the second statement
So in this case, I used a subquery to get the initial results, and then used a join.
SELECT table1.nisinfo.* FROM table1.nisinfo JOIN (SELECT distinct(fqhn) FROM table2.hosts WHERE package = 'bash') AS FQ ON ((SUBSTRING_INDEX(FQ.fqhn, '.', 1)) = table1.nisinfo.shortname);

LEFT JOIN MYSQL full text search - search multiple columns cross table

I have two tables:
Table 1: Content [title, description, url, genre_id]
Table 2: Genre [genre_id, genre_name]
I'm trying to run a FULL SEARCH query so that if someone searches for a genre, the details from the Content will be returned. Equally if someone searches for a title, the Content will be returned as normal. Note that some Contents do not have a genre assigned so it will need to work in this scenario.
This is my query - I just can't work out how to get it working...
"SELECT content.*, genre.* FROM content LEFT OUTER JOIN genre
ON (genre.genre_id=content.genre_id)
WHERE MATCH (content.title, content.description)
AGAINST ('$query')
OR MATCH (genre.genre_name)
AGAINST ('$query')
AND content.url IS NOT NULL AND content.url <> '' LIMIT $lowerlimit, 10";
If you have Content items that don't have genre_id's, then INNER JOIN is not the way to go. That will exclude any Content items that don't have genre-id's. Use a LEFT OUTER JOIN instead. This assumes that there are no Genres whose genre_id's don't have a corresponding Content item. If the latter is the case, you need to create some kind of dummy row in your Content table to handle this. If this doesn't make sense, let me know and I'll give you more detail.
I am not familiarized with the WHERE MATCH ** AGAINST **, but I believe it could be rewritten to:
SELECT content.*, genre.*
FROM content
LEFT OUTER JOIN genre
ON genre.genre_id = content.genre_id
WHERE (content.title LIKE '%' + '$query' + '%'
OR content.description LIKE '%' + '$query' + '%'
OR genre.genre_name LIKE '%' + '$query' + '%')
AND content.url IS NOT NULL
AND content.url <> ''
LIMIT $lowerlimit, 10;
I also noted in your query there's a additional double quote at the end, before the semicolon.

Searching two mysql columns added together for one variable

I currently have a search form which should allow a user to search for a customers full name and it will return the row.
For Example: A user searches for "Mr. N Mallow" and it will return the row which matches that query. Since I am new to MySQL I need some help, I've tried + but that has no effect, probably because it's not standard mysql or something like that.
select *
from mooring
left join customer
on mooring.assignedTo = customer.id
where mooring.Number like \"$var\"
or (customer.TitleName + customer.Surname = '$var')
Any suggestions?
select * from mooring
left join customer on mooring.assignedTo = customer.id
where mooring.Number like \"$var\" OR (customer.TitleName + customer.Surname = '$var')
Try CONCAT_WS or CONCAT, which join strings together (the first version is "with separator"):
CONCAT(customer.TitleName,' ',customer.Surname)
or
CONCAT_WS(' ',customer.TitleName,customer.Surname)

need some help optimizing a stored proc

I have a stored procedure which is building a dynamic sql query and then running it via exec(#sql).
The stored proc is joining about 12 tables. As it was, it was running relatively quickly. But then i needed to added in an additional field. To do this, i created a scalar function, which looks like this:
SELECT #weight = #weight +COUNT(*) FROM dbo.UserPDMedication WHERE UserID = #userid
SELECT #weight = #weight +COUNT(*) FROM dbo.[User] WHERE UserID = #userid AND HoehnYarhID IS NOT null
SELECT #weight = #weight +COUNT(*) FROM dbo.[User] WHERE UserID = #userid AND DateOfBirth IS NOT NULL
SELECT #weight = #weight +COUNT(*) FROM dbo.[User] WHERE UserID = #userid AND GenderID IS NOT NULL
SELECT #weight = #weight +COUNT(*) FROM dbo.[User] WHERE UserID = #userid AND DateDiagnosed IS NOT null
It's basically just a function that will return an int based on how many questions a user has filled out. So for each user in the stored proc, this function gets called. The stored proc looks like this:
SELECT DISTINCT u.UserID, u.Healthy, u.DateOfBirth, u.City, st.StateCode AS State, u.GenderID, g.Gender, u.Latitude, u.Longitude, u.PDConditionID, u.Zip, u.Distance,
(SELECT TOP 1 EmailID FROM Messages m WHERE TrialID = ' + #trialID + ' AND ToUserID = u.userid AND LocationID = ' + #locationID + ') AS MessageID, dbo.UserWeightedValue(u.UserID) as wt
FROM [User] u
INNER JOIN aspnet_UsersInRoles uir ON u.AspnetUserID = uir.UserId
INNER JOIN aspnet_Roles r ON uir.RoleId = r.RoleId
FULL JOIN UserHealthCondition uhc ON u.UserID = uhc.UserID
FULL JOIN UserMotorSymptom ums ON u.UserID = ums.UserID
FULL JOIN UserNonMotorSymptom unms ON u.UserID = unms.UserID
FULL JOIN UserPDMedication updm ON u.UserID = updm.UserID
FULL JOIN UserPDTreatment updt ON u.UserID = updt.UserID
FULL JOIN UserSupplement us ON u.UserID = us.UserID
FULL JOIN UserPDGeneticMarker updgm ON u.UserID = updgm.UserID
FULL JOIN UserFamilyMember ufm ON u.UserID = ufm.UserID
FULL JOIN State st ON u.StateID = st.ID
FULL JOIN Gender g ON u.GenderID = g.ID
WHERE u.UserID IS NOT NULL
(i removed some chunks to try and keep this short). This get's executed as a dynamic string in the stored proc. Any tips on how i can optimize this to speed things up?
Thanks
EDIT: i got this working using a combination of suggestions here. I kept my function as is although i combined the multiple select statements into 2 statements.I then took the original stored proc and changed the select to a select into ##temp. And then i ran my function against that temp table. Execution time dropped down to 3-4 seconds. I think I will have to give credit to grant for this question since it was his pointing out distinct that put me on the right trail. But thank you to everyone.
The DISTINCT is absolutely going to cause a performance hit as it does aggregations. Do you really need it? Frequently when you see DISTINCT it's an indication of a data or structural issue that is getting papered over by the ability to eliminate duplicates that the structure should elminate on it's own.
After that, instead of a correlated query in the SELECT list, I'd look to move that as a JOIN. It's not a sure fire win, but frequently the optimizer is better able to work that into the plan.
Based on the complexity of what you're presenting, I'd also look at the execution plan. First thing to check, do you have a full optimization or did it timeout. If it timed out, then you're dealing with a best guess, not a fully calculated "good enough" plan. If that's so, you need to look at simpllifying this query. If you have a good enough plan, see where the bottlenecks are within it.
If UserID is the primary key of the table User, then there is no need to do one SELECT for question filled by the user, you can wrap it in just one SELECT:
SELECT #weight = #weight + COUNT(HoehnYarhID) + COUNT(DateOfBirth) + COUNT(GenderID) + COUNT(DateDiagnosed)
FROM dbo.[User]
WHERE UserID = #userid
Convert the scalar valued function into an inline table valued function.
Scalar functions, inlining, and performance