Is it possible to perform such a queries - sql

I have SQL Server tables and I am supposed to create a query where it will be able to draw out the same data based on any combination of inputs of parameters / search conditions in a single query
For example, a user gives a model number for us to search for all products in table 1.
I would create a query as follows:
SELECT *
FROM Products
WHERE pModel = #pUserModel;
Next, another user gives us a description of the product which I then have to create another query as follows:
SELECT *
FROM Products
WHERE pDescription = #pUserDescription;
Table 1
Products (pNo, pModel, pDescription, pQty, pGlobalQty, pParent)
My issue here would be that instead of the two queries that were created based on different parameters, is there any way to create a single query where it will be able to fulfill those two search condition by both different users?

declare #pUserModel varchar(50)
declare #pUserDescription varchar(50)
--- if you want to search with individual search parameter than try with this.
if (#pUserModel <> '')
begin
SELECT * FROM Products WHERE pModel = #pUserModel;
end
else if (#pUserDescription <> '')
begin
SELECT * FROM Products WHERE pDescription = #pUserDescription;
end
--- if you want to combine all search parameter than try with this.
SELECT * FROM Products WHERE LTRIM(RTRIM(pModel)) = #pUserModel or LTRIM(RTRIM(pDescription)) = #pUserDescription

If I understand your requirement properly, you need join and OR condition
select * from Products p
left join Model m
on p.pModel=m.mModel
where (p.pModel = #pUserModel
OR p.pDescription = #pUserDescription)
Left join to return records from Products even if there is no Model associated with it. You can change it to INNER JOIN if you don't want it.

You can look for the trimmed length of the parameter string as part of an OR condition and always provide both parameters, even if one of them is an empty string "". Something like
SELECT
P.*
from
Products P
where
( LEN( RTRIM( #pUserModel )) = 0
OR P.pModel = #pUserModel )
OR
( LEN( RTRIM( #pUserDescription )) = 0
OR P.pDescription = #pUserDescription )
if you query such as where pDescription = '', it would fail if the description column actually HAS a value. So by wrapping each in an OR condition to say it's ok if the length of the TRIMMED parameter = 0 OR it matches the one being searched for. If you want the wild-card search, be sure to add '%'
To eliminate the OR qualifier, if your empty parameters are just '%', that will pull everything, for that respective parameters. So you could have a model = '%' and description = 'Something%'... Or vice-versa... Model = 'Some Model%' and Description = '%'
I aliased the products table as "P" to simplify readability. Especially if you have multiple tables, you should always identify where a table/column comes from for others to help.

Related

How can you use a string variable that has SQL inside a SQL LIKE wildcard pattern expression?

I have a SELECT statement with a WHERE clause that uses a LIKE wildcard with an expression coming from a variable string that I have built using values from a table. However, although there are no errors, the query is not evaluating correctly to give the expected results, as it is returning all the results records and not applying the condition in the LIKE wildcard. The desired result should be fewer records returned as they will be filtered against the LIKE wildcard expression.
The variable I have built to use in the SQL query's LIKE wildcard is the following, along with the value of the string:
Declare #TrimmedAgeType Varchar(450);
The value is this: '%J%' AND PERM.AgeFilter NOT LIKE '%JS%'
The SQL query looks like this:
SELECT * FROM #MembershipInfo #PSP
INNER JOIN TRP_ME_PriceStructure PERM
ON #PSP.MembershipPriceStructurePK = PERM.MembershipPriceStructurePK
WHERE PERM.AgeFilter NOT LIKE + #TrimmedAgeType
When I hardcode the query with the string value below, I get the expected results returned back.
SELECT * FROM #MembershipInfo #PSP
INNER JOIN TRP_ME_PriceStructure PERM
ON #PSP.MembershipPriceStructurePK = PERM.MembershipPriceStructurePK
WHERE PERM.AgeFilter NOT LIKE + '%J%' AND PERM.AgeFilter NOT LIKE '%JS%'
If you want to know how I am initially building the string variable value, see below:
Declare #AgeType Varchar(450);
Declare #AgeReference varchar(450)
SET #AgeType = CONCAT(#AgeType, '''%' + #AgeReference + '%''') + ' AND PERM.AgeFilter NOT LIKE '
Additional Info:
This #AgeType variable is being generated inside a while loop
that is looping through a table of Age References. I later change the
name of the variable to #TrimmedAgeType as I do some additional work on it to remove the last
occurrence of ' AND PERM.AgeFilter NOT LIKE '
More Info:
I have a table of memberships with the AgeFilter column containing letter references ('A,U,J,C,JS', etc). I want to return all results that do not match the given references in the LIKE wildcard pattern. This is because the SELECT query is actually going to be a DELETE query and I will delete all records that are returned and do not match the given references in the LIKE wildcard pattern.
Hope someone can help. Thank you.
Let's say we have some filters stored here:
CREATE TABLE dbo.FilterPatterns
(
PatternID int PRIMARY KEY,
FilterPattern varchar(32) NOT NULL UNIQUE
);
To simulate the question, let's say we know the PatternID values we're after are 1 and 3:
INSERT dbo.FilterPatterns(PatternID, FilterPattern)
VALUES(1,'%J%'),(2,'%Q%'),(3,'%JS%');
We've got some members stored in a #temp table for reasons unknown (how is that populated?):
CREATE TABLE #MembershipInfo
(
MembershipPriceStructurePK int PRIMARY KEY
);
Let's insert a few rows to match and not match (note: 30 doesn't exist in the core table).
INSERT #MemberShipInfo VALUES(5),(10),(15),(20),(30);
Two of these will have matching age filters in the core table, the other two will not. We'll also add a row not in the temp table (25):
CREATE TABLE dbo.TRP_ME_PriceStructure
(
MembershipPriceStructurePK int PRIMARY KEY,
AgeFilter varchar(256)
);
INSERT dbo.TRP_ME_PriceStructure VALUES
(5,'foo'),(10,'BobJoined'),(15,'blat'),(20,'NodeJS'),(25,'funky');
Now our query can just do a NOT EXISTS against the filter patterns table using whatever logic you're currently using to pull values from that table to build the string:
SELECT * FROM #MembershipInfo #PSP
INNER JOIN dbo.TRP_ME_PriceStructure PERM
ON #PSP.MembershipPriceStructurePK = PERM.MembershipPriceStructurePK
WHERE NOT EXISTS
(SELECT 1 FROM
dbo.FilterPatterns AS fp
WHERE fp.PatternID IN (1,3)
AND PERM.AgeFilter LIKE fp.FilterPattern);
members 5 and 15 are returned
members 10 and 20 are left out because they matched the filter
members 25 and 30 are left out because they miss the join
Example db<>fiddle
Try to hard code the wildcard
SELECT * FROM #MembershipInfo #PSP
INNER JOIN TRP_ME_PriceStructure PERM
ON #PSP.MembershipPriceStructurePK =
PERM.MembershipPriceStructurePK
WHERE PERM.AgeFilter NOT LIKE + '%'+ #TrimmedAgeType + '%'
Using Dynamic SQL I have successfully written the query, returning me the expected results:
DECLARE #DeleteUnsuitableMemberships varchar(MAX)
SET #DeleteUnsuitableMemberships =
'DELETE #MembershipInfo FROM #MembershipInfo #PSP
INNER JOIN TRP_ME_PriceStructure PERM
ON #PSP.MembershipPriceStructurePK = PERM.MembershipPriceStructurePK
WHERE PERM.AgeFilter NOT LIKE ' + #TrimmedAgeType
EXEC(#DeleteUnsuitableMemberships)
I did was the following:
Declare a new variable.
Set that variable to the SQL query which I have wrapped in apostrophe (').
Appended the variable using the + operator.
Used the EXEC() function with the variable placed inside as a parameter.
The value of #TrimmedAgeType is still '%J%' AND PERM.AgeFilter NOT LIKE '%JS%'
Thank you all for helping.

Selecting all data if the string is empty in SQL IN operator

I have a stored procedure for the Filters of products in my website which goes like:
ALTER PROCEDURE [dbo].[sp_product_get_by_filters]
(#brand_names nvarchar(max),
#type nvarchar(max))
AS
BEGIN
SELECT
tbl_product.product_code,
tbl_product.brand_name,
tbl_product.subcategory_code,
tbl_product.product_name,
tbl_product.product_photo_1,
tbl_product.filter_code,
(select filter_name from tbl_filter where filter_code = tbl_product.filter_code )as filter_name,
(select AVG(CAST(rating AS DECIMAL(10,2))) from tbl_review where product_code = tbl_product.product_code) as Rating,
(select TOP 1 sub_product_price from tbl_sub_product where product_code = tbl_product.product_code) as product_price,
(select TOP 1 size from tbl_sub_product where product_code = tbl_product.product_code) as size,
(select TOP 1 sub_product_code from tbl_sub_product where product_code = tbl_product.product_code) as sub_product_code
FROM
tbl_product
WHERE
tbl_product.brand_name IN (SELECT * FROM dbo.splitstring(#brand_names))
AND tbl_product.filter_code IN (SELECT * FROM dbo.splitstring(#type))
END
#brand_names here is a string of the name of brands separated by comma for example
Apple,Samsung,Nokia
and #type is the filter of the products which is like
'Watch,Mobile,Tablet'
The dbo.splitstring function separates each value from the concatenated string and return the list as a table. So the problem when a User select both Brand Name and Type the query returns the values but if a user select only Brand Name or Type the query doesn't return anything. I want to make the query to return the products if the user select both Brand Name and Type or don't select any of them (You know like filters in every e-commerce website). If the user doesn't select any filter I am passing an empty string in variables like if user doesn't select any brand then #brand_names will be #brand_names = ''.
For example if a user select Brand Name Apple the query must return all the products related to this brand. And again if the user select the Type watch then the query must return the Watches from brand Apple. I am using SQL Server 2008.
Thank you for the Help.
For this kind of "optional parameter" query, an option recompile at the end can improve performance by quite a lot.
If an "unselected" parameter is an empty string, then you can do:
WHERE
(#brand_names = '' or tbl_product.brand_name IN (SELECT * from dbo.splitstring(#brand_names)))
and (#type = '' or tbl_product.filter_code IN (SELECT * from dbo.splitstring(#type)))
option (recompile)
The option (recompile) tells SQL to build a new plan for this statement every time the procedure runs. So, for example, if you pass an empty string for #brand_names, the engine doesn't even need to evaluate the or tbl_product.brand_name in ... part of that predicate. If you don't do this, then SQL will - as always - build a plan for the first execution, and then reuse that plan on subsequent executions. That's not great when different parameter values can make such a big difference to the result.

Is putting a OR condition when Selecting/Displaying columns valid?

new to SQL, I was wondering if its valid to put a "or" condition when choosing columns (SELECT)?
Example:
SELECT (Column1 OR Column2)
My Aim:
I'm currently creating a view that will display all claim types basic information that I will use for queries. Obviously, they are in different tables, so I was hoping to merge them into one view for easy access. The problem is when created the view puts the columns with OR statement to either a 1 or 0, when it should be a VARCHAR (string).
Full SQL Query:
CREATE VIEW ViewAllClaims AS
SELECT Claim.ClaimID, Claim.CreatedDate, (ReturnClaim.ClaimStatus or RepairClaim.ClaimStatus) as 'ClaimStatus', (SupplierName or FirstName) as 'Name'
FROM Claim, ReturnClaim, RepairClaim, Supplier, Product, ReturnClaimProduct, Customer
WHERE ( Claim.ClaimID = ReturnClaim.ClaimID
AND ReturnClaim.ClaimID = ReturnClaimProduct.ClaimID
AND ReturnClaimProduct.Keycode = Product.Keycode
AND Product.SupplierID = Supplier.SupplierID)
or
(Claim.ClaimID = RepairClaim.ClaimID
AND RepairClaim.CustomerID = Customer.CustomerID);
Screenshot of the result:
You can use CASE:
SELECT (
CASE
WHEN ReturnClaim.ClaimStatus IS NULL THEN RepairClaim.ClaimStatus
ELSE ReturnClaim.ClaimStatus
END
) AS 'ClaimStatus'
FROM ...

Oracle where clause that matches all entries

Is there a way to construct a parametrised query with a where clause on a field that matches all entries in the field. For example I am imagining syntax like
'select brands from cars where brands = param'
such that if param = 'Toyota' then it only returns rows matching Toyota, but if param = * it returns all rows from the cars table.
Try this. It's a conditional WHERE clause
WHERE brands = NVL(param, brands)
Means if the param is NULL, use brands = brands
The usual workaround is
select brands from cars where brands LIKE param
If param = 'Toyota' it returns only Toyota
If param = '%' it returns everything
Simple but not necessarily performant.
select * from cars where brands like '%'||parameter||'%'
You can use distinct to get all brands from your table.
If you have a variable, you would do:
where v_brand is null or v_brand = brand
If you just want an expression that matches everything:
where 1 = 1
The proper way to return all rows regardless of brand is to remove the brands = param from your filter, not to use a wildcards or functions on it...
It is common to add where 1=1 to clauses in your case and use in the following pattern:
If param has a value
select brands from cars where 1=1 and brands = param
otherwise you just remove the and brands = param part:
select brands from cars where 1=1

SQL inner join list split for an SSRS report

I have a field that lists all language descriptions that a product has and the field can contain data like:
EN;FR;DE
It will always be a two letter language code followed by a semi colon.
I then have a stored procedure that looks for all products with a particular language code. Simply done by:
WHERE
ext.languages LIKE '%' + #language + '%'
The #language variable might just represent the letters EN for example. Now when I want to find a product that has both French and English languages on I need to pass in 'FR, EN' for the language variable. Now I have a custom function in SQL that splits the language variable into rows so I effectively have
Row 1-EN
Row 2-FR
Now I need to check my ext.language field to see if both those values exist.
I have attempted to do:
INNER JOIN MyFunctionsDatabase.dbo.listSplit(#language) as var1
ON ext.language LIKE '%'+var1.StringLiteral+'%'
This only brings back products where it contains either french or english I need it to bring back values where it contains both English and French.
Any advice would be greatly appreciated:
Try with below script, this i write for 3 language but can be done generic
Declare #Product AS Table(ProductID INT, [Language] Varchar(500))
Insert Into #Product Values(1,'EN;FR;DE'),(2,'EN'),(3,'EN;DE'),(4,'EN;FR')
SELECT * FROM
(
Select P.ProductID,L.Value
From #Product P
CROSS APPLY dbo.[udfSplit]([Language],';') L
) Product
PIVOT
(
Count(Value)
For Value in (EN,FR,DE)
)
AS PV
Where EN=1 AND FR=1
I'd be inclined to use a function that accepts a delimited string containing the language codes to check for and the string to check. It checks that each language code is in the string and returns false as soon as one of the desired languages isn't found. If everything is found it returns true.
Your sql would look like
select *
from mytable
where CheckHasAllLanguages(language, #languagesToCheck) = 1
I would make your parameter a multi-select and have each individual language be a selection. You could even feed the parameter with values from the database so it would automatically update if there is a new language. I'm going to call this parameter #LangMultiSelect
Since you only want items that items that match all of the selections you need to pass in a second parameter with the number of items that have been selected. In the properties of your dataset you can add another parameter that is set by an expression. Name it #LangCount and use the expression:
=Parameters!LangMultiSelect.Count
Then use a SQL query similar to this:
SELECT Name
FROM (
SELECT Name,
COUNT(*) OVER(PARTITION BY pt.id) AS lCount
FROM ProductTable AS pt
INNER JOIN MyFunctionsDatabase.dbo.listSplit(#language) AS var1 ON var1.id=pt.id
WHERE pt.language IN (#LangMultiSelect)
) AS t
WHERE t.lCount = #LangCount
That query uses the COUNT() aggregate as a window function to determine the number of matches the item has and then only returns results that match all of the selections in the multi-select parameter.
It works because I am splitting the count by a field that is the same for all of the item names that are the same item but in a different language. If you don't have a field like that this won't work.