Sql function to make noun plural - sql

Is there any function in SQL Server which change the noun from singular to plural form?

SQL itself doesn't have anything like this - but you could try to use the .NET PluralizationService introduced in .NET 4 - the same functionality that the Entity Framework uses to pluralize/singularize table names to object names.
You would have to write a SQL-CLR assembly to tap into the pluralization services, but it definitely seems like a doable thing!

That function does not exist in SQL Server.

The function does not exist in SQL Server, as #aF mentioned. The one place I know if it existing is in Entity Framework 4+. The pluralization object can actually be instantiated and used.
SQL has the capacity to run CLR code--through SQL CLR. The main problem is that SQL CLR is limited to the .NET Framework 3.5. So you would either need to write some .net 4 code that operates on your tables. Alternately, you could use a product like Reflector and reverse engineer a 3.5 compatible version and run it inside SQL Server.

CREATE FUNCTION dbo.Pluralize
(
#noun nvarchar(50)
)
RETURNS nvarchar(50)
AS
BEGIN
DECLARE #QueryString nvarchar(4000)
SET #QueryString = N'FORMSOF(INFLECTIONAL,"' + #noun + N'")'
RETURN
(SELECT TOP 1 display_term
FROM sys.dm_fts_parser(#QueryString,1033,0,0))
END
GO
SELECT noun,
dbo.Pluralize(noun)
FROM (VALUES('cat'),
('mouse'),
('goose'),
('person'),
('man'),
('datum')) nouns(noun)
Returns
noun
------ ------------------------------
cat cats
mouse mice
goose geese
person persons
man men
datum data
Unfortunately it just relies on observation that the TOP 1 expansion term for the noun is the plural form. I doubt that this is documented anywhere.

DECLARE #PluralVersion nvarchar(128) = ''
DECLARE #TableName nvarchar(128) = 'MyTableName'
DECLARE #NounVersions TABLE(Term nvarchar(128) NOT NULL)
DECLARE #QueryString nvarchar(4000) SET #QueryString = N'FORMSOF(INFLECTIONAL,"' + #TableName + N'")'
INSERT INTO #NounVersions
SELECT TOP 10 display_term FROM sys.dm_fts_parser(#QueryString,1033,0,0)
SELECT TOP 1 #PluralVersion = Term FROM #NounVersions WHERE Term Not Like '%''%' AND RIGHT(Term,1) = 's'
SET #PluralVersion = UPPER(LEFT(#PluralVersion,1))+LOWER(SUBSTRING(#PluralVersion,2,LEN(#PluralVersion)))
SELECT #PluralVersion
I know this is an old thread, but thought I would post anyways. Based on what Martin started, I expanded it and so far it is doing an OK job. I would not say it is perfect. This is used for pluralizing table names so it is very controlled

Do you want to use this for display purposes?
Something like "Your search returned 1 result" / "Your search returned 4 results" ?
If yes, I wouldn't do it like that.
Finding or writing a function that does this correctly for all special cases (let alone in several languages) is nearly impossible, and storing each needed text once in singular and once in plural form isn't much better.
At work, I'm dealing with multiple languages and lots of dynamically generated sentences like this a lot, and I found that completely avoiding the distinction of singular/plural forms at all is the easiest solution to manage:
"Number of results for this search: 1"

Nope, but it would be pretty easy to make a table for this if you have a limited set of words to check.
Example:
CREATE TABLE dbo.Plurals
(
id int IDENTITY,
singular varchar(100),
plural varchar(100)
)
INSERT INTO dbo.Plurals
VALUES
('cat', 'cats'),
('goose', 'geese'),
('man', 'men'),
('question', 'questions')
Alternatively, you could make the table just be exceptions, i.e. words that can't be pluralized with the simple addition of an s - then you could do an EXISTS check on that table, if it's not there then add an s and if it is then lookup the plural.

I wrote this. Probably not perfect but it works for me. And it returns with same capitalization as it was called.
create function dbo.udfPlural(#Int int
, #Item varchar(47))
returns varchar(50)
as
begin
-- VARS...
declare #Ninth char(1)
declare #Tenth char(1)
declare #Reply varchar(50)
-- ONLY FOR PLURALS...
if (#Int <> 1)
begin
-- LAST AND PENULTIMATE LETTERS...
set #Ninth = ''
set #Tenth = ''
if (len(#Item) >= 2) set #Ninth = substring(#Item, len(#Item) - 1, 1)
if (len(#Item) >= 1) set #Tenth = substring(#Item, len(#Item), 1)
-- APPLY S/IES...
if (#Tenth = 's')
set #Reply = #Item + 'es'
else if (#Tenth = 'y')
if (charindex(#Ninth, 'AEIOU') > 0)
set #Reply = #Item + 's'
else
set #Reply = substring(#Item, 1, len(#Item) - 1) + 'ies'
else
set #Reply = #Item + 's'
end
-- ASSEMBLE...
set #Reply = ltrim(str(#Int)) + ' ' + #Reply
-- DONE...
return #Reply
end

Related

SQL Server 2012: Remove text from end of string

I'm new to SQL so please forgive me if I use incorrect terminology and my question sounds confused.
I've been tasked with writing a stored procedure which will be sent 3 variables as strings (varchar I think). I need to take two of the variables and remove text from the end of the variable and only from the end.
The strings/text I need to remove from the end of the variables are
co
corp
corporation
company
lp
llc
ltd
limited
For example this string
Global Widgets LLC
would become
Global Widgets
However it should only apply once so
Global Widgets Corporation LLC
Should become
Global Widgets Corporation
I then need to use the altered variables to do a SQL query.
This is to be used as a backup for an integration piece we have which makes a callout to another system. The other system takes the same variables and uses Regex to remove the strings from the end of variables.
I've tried different combinations of PATINDEX, SUBSTRING, REPLACE, STUFF but cannot seem to come up with something that will do the job.
===============================================================
Edit: I want to thank everyone for the answers provided so far, but I left out some information that I didn't think was important but judging by the answers seems like it would affect the processing.
My proc will start something like
ALTER PROC [dbo].[USP_MyDatabaseTable] #variableToBeAltered nvarchar(50)
AS
I will then need to remove all , and . characters. I've already figured out how to do this. I will then need to do the processing on #variableToBeAltered (technically there will be two variables) to remove the strings I listed previously. I must then remove all spaces from #variableToBeAltered. (Again I figured that part out). Then finally I will use #variableToBeAltered in my SQL query something like
SELECT [field1] AS myField
,[field2] AS myOtherField
FROM [MyData].[dbo].[MyDatabaseTable]
WHERE [field1] = (#variableToBeAltered);
I hope this information is more useful.
I'd keep all of your suffixes in a table to make this a little easier. You can then perform code like this either within a query or against a variable.
DECLARE #company_name VARCHAR(50) = 'Global Widgets Corporation LLC'
DECLARE #Suffixes TABLE (suffix VARCHAR(20))
INSERT INTO #Suffixes (suffix) VALUES ('LLC'), ('CO'), ('CORP'), ('CORPORATION'), ('COMPANY'), ('LP'), ('LTD'), ('LIMITED')
SELECT #company_name = SUBSTRING(#company_name, 1, LEN(#company_name) - LEN(suffix))
FROM #Suffixes
WHERE #company_name LIKE '%' + suffix
SELECT #company_name
The keys here are that you are only matching with strings that end in the suffix and it uses SUBSTRING rather than REPLACE to avoid accidentally removing copies of any of the suffixes from the middle of the string.
The #Suffixes table is a table variable here, but it makes more sense for you to just create it and fill it as a permanent table.
The query will just find the one row (if any) that matches its suffix with the end of your string. If a match is found then the variable will be set to a substring with the length of the suffix removed from the end. There will usually be a trailing space, but for a VARCHAR that will just get dropped off.
There are still a couple of potential issues to be aware of though...
First, if you have a company name like "Watco" then the "co" would be a false positive here. I'm not sure what can be done about that other than maybe making your suffixes include a leading space.
Second, if one suffix ends with one of your other suffixes then the ordering that they get applied could be a problem. You could get around this by only applying the row with the greatest length for suffix, but it gets a little more complicated, so I've left that out for now.
Building on the answer given by Tom H, but applying across the entire table:
set nocount on;
declare #suffixes table(tag nvarchar(20));
insert into #suffixes values('co');
insert into #suffixes values('corp');
insert into #suffixes values('corporation');
insert into #suffixes values('company');
insert into #suffixes values('lp');
insert into #suffixes values('llc');
insert into #suffixes values('ltd');
insert into #suffixes values('limited');
declare #companynames table(entry nvarchar(100),processed bit default 0);
insert into #companynames values('somecompany llc',0);
insert into #companynames values('business2 co',0);
insert into #companynames values('business3',0);
insert into #companynames values('business4 lpx',0);
while exists(select * from #companynames where processed = 0)
begin
declare #currentcompanyname nvarchar(100) = (select top 1 entry from #companynames where processed = 0);
update #companynames set processed = 1 where entry = #currentcompanyname;
update #companynames
set entry = SUBSTRING(entry, 1, LEN(entry) - LEN(tag))
from #suffixes
where entry like '%' + tag
end
select * from #companynames
You can use a query like below:
-- Assuming that you can maintain all patterns in a table or a temp table
CREATE TABLE tbl(pattern varchar(100))
INSERT INTO tbl values
('co'),('llc'),('beta')
--#a stores the string you need to manipulate, #lw & #b are variables to aid
DECLARE #a nvarchar(100), #b nvarchar(100), #lw varchar(100)
SET #a='alpha beta gamma'
SET #b=''
-- #t is a flag
DECLARE #t int
SET #t=0
-- Below is a loop
WHILE(#t=0 OR LEN(#a)=0 )
BEGIN
-- Store the current last word in the #lw variable
SET #lw=reverse(substring(reverse(#a),1, charindex(' ', reverse(#a)) -1))
-- check if the word is in pattern dictionary. If yes, then Voila!
SELECT #t=1 FROM tbl WHERE #lw like pattern
-- remove the last word from #a
SET #a=LEFT(#a,LEN(#a)-LEN(#lw))
IF (#t<>1)
BEGIN
-- all words which were not pattern are joined back onto this stack
SET #b=CONCAT(#lw,#b)
END
END
-- get back the remaining word
SET #a=CONCAT(#a,#b)
SELECT #a
drop table tbl
Do note that this method overcomes Tom's problem of
if you have a company name like "Watco" then the "co" would be a false positive here. I'm not sure what can be done about that other than maybe making your suffixes include a leading space.
use the replace function in SQL 2012,
declare #var1 nvarchar(20) = 'ACME LLC'
declare #var2 nvarchar(20) = 'LLC'
SELECT CASE
WHEN ((PATINDEX('%'+#var2+'%',#var1) <= (LEN(#var1)-LEN(#var2)))
Or (SUBSTRING(#var1,PATINDEX('%'+#var2+'%',#var1)-1,1) <> SPACE(1)))
THEN #var1
ELSE
REPLACE(#var1,#var2,'')
END
Here is another way to overcome the 'Runco Co' situation.
declare #var1 nvarchar(20) = REVERSE('Runco Co')
declare #var2 nvarchar(20) = REVERSE('Co')
Select REVERSE(
CASE WHEN(CHARINDEX(' ',#var1) > LEN(#var2)) THEN
SUBSTRING(#var1,PATINDEX('%'+#var2+'%',#var1)+LEN(#var2),LEN(#var1)-LEN(#var2))
ELSE
#var1
END
)

update table with dynamic sql query

For a project, we are using a table (named txtTable) that contains all the texts. And each column contains a different language (for example column L9 is English, column L7 is German, etc..).
TextID L9 L7 L16 L10 L12
------------------------------------------------------
26 Archiving Archivierung NULL NULL NULL
27 Logging Protokollierung NULL NULL NULL
28 Comments Kommentar NULL NULL NULL
This table is located in a database on a Microsoft SQL Server 2005. The big problem is that this database name changes each time the program is restarted. This is a behavior typically for this third-party program and cannot be changed.
Next to this database and on the same server is our own database. In this database are several tables that point to the textID for generating data for reporting (SQL Server Reporting Services) in the correct language. This database contains also a table "ProjectSettings" with some properties like the name of the texttable database, and the stored procedures to generate the reporting data.
The way we now are requesting the right texts of the right language from this table with the changing database name is by creating a dynamic SQL query and execute it in a stored procedure.
Now we were wondering if there is a cleaner way to get the texts in the right language. We were thinking about creating a function with the textID and the language as a parameter, but we cannot find a good way to do this. We thought about a function so we just can use it in the select statement, but this doesn’t work:
CREATE FUNCTION [dbo].[GetTextFromLib]
(
#TextID int,
#LanguageColumn Varchar(5)
)
RETURNS varchar(255)
AS
BEGIN
-- return variables
DECLARE #ResultVar varchar(255)
-- Local variables
DECLARE #TextLibraryDatabaseName varchar(1000)
DECLARE #nvcSqlQuery varchar(1000)
-- get the report language database name
SELECT #TextLibraryDatabaseName = TextLibraryDatabaseName FROM ProjectSettings
SET #nvcSqlQuery = 'SELECT #ResultVar =' + #LanguageColumn + ' FROM [' + #TextLibraryDatabaseName + '].dbo.TXTTable WHERE TEXTID = ' + cast(#TextID as varchar(30))
EXEC(#nvcSqlQuery)
-- Return the result of the function
RETURN #ResultVar
END
Is there any way to work around this so we don’t have to use the dynamic sql in our stored procedures so it is only ‘contained’ in 1 function?
Thanks in advance & kind regards,
Kurt
Yes, it is possible with the help of synonym mechanism introduced with SQL Server 2005. So, you can create synonym during your setting up procedure based on data from ProjectSettings table and you can use it in your function. Your code will look something like this:
UPDATE: The code of function is commented here because it still contains dynamic SQL which does not work in function as Kurt said in his comment. New version of function is below this code.
-- Creating synonym for TXTTable table
-- somewhere in code when processing current settings
-- Suppose your synonym name is 'TextLibrary'
--
-- Drop previously created synonym
IF EXISTS (SELECT * FROM sys.synonyms WHERE name = N'TextLibrary')
DROP SYNONYM TextLibrary
-- Creating synonym using dynamic SQL
-- Local variables
DECLARE #TextLibraryDatabaseName varchar(1000)
DECLARE #nvcSqlQuery varchar(1000)
-- get the report language database name
SELECT #TextLibraryDatabaseName = TextLibraryDatabaseName FROM ProjectSettings
SET #nvcSqlQuery = 'CREATE SYNONYM TextLibrary FOR [' + #TextLibraryDatabaseName + '].dbo.TXTTable'
EXEC(#nvcSqlQuery)
-- Synonym created
/* UPDATE: This code is commented but left for discussion consistency
-- Function code
CREATE FUNCTION [dbo].[GetTextFromLib]
(
#TextID int,
#LanguageColumn Varchar(5)
)
RETURNS varchar(255)
AS
BEGIN
-- return variables
DECLARE #ResultVar varchar(255)
-- Local variables
DECLARE #nvcSqlQuery varchar(1000)
SET #nvcSqlQuery = 'SELECT #ResultVar =' + #LanguageColumn + ' FROM TextLibrary WHERE TEXTID = ' + cast(#TextID as varchar(30))
EXEC(#nvcSqlQuery)
-- Return the result of the function
RETURN #ResultVar
END
*/
UPDATE This is one more attempt to solve the problem. Now it uses some XML trick:
-- Function code
CREATE FUNCTION [dbo].[GetTextFromLib]
(
#TextID int,
#LanguageColumn Varchar(5)
)
RETURNS varchar(255)
AS
BEGIN
-- return variables
DECLARE #ResultVar varchar(255)
-- Local variables
DECLARE #XmlVar XML
-- Select required record into XML variable
-- XML has each table column value in element with corresponding name
SELECT #XmlVar = ( SELECT * FROM TextLibrary
WHERE TEXTID = #TextID
FOR XML RAW, ELEMENTS )
-- Select value of required element from XML
SELECT #ResultVar = Element.value('(.)[1]', 'varchar(255)')
FROM #XmlVar.nodes('/row/*') AS T(Element)
WHERE Element.value('local-name(.)', 'varchar(50)') = #LanguageColumn
-- Return the result of the function
RETURN #ResultVar
END
Hope this helps.
Credits to answerer of this question at Stackoverflow - How to get node name and values from an xml variable in t-sql
To me, it sounds like a total PITA... However, how large is this database of "words" you are dealing with. Especially if it is not changing much and remains pretty constant. Why not have on some normal cycle (such as morning), just have one dynamic query generated that queries the one that changes and synchronize it to a "standard" table name in YOUR database that won't change. Then, all your queries run against YOUR version and completely remove the constant dynamic queries every time. Yes there would need to be this synchronizing stored procedure to run, but if it can be run on a schedule, you should be fine, and again, how large is the table of "words" for proper language context.

Selecting only numbers from a string

We have a program that can pull information from a database that we use for shipping. The way it works is it uses an ODBC driver to pull from our database, so that when we type in "order number 5" into the shipping program it will also pull the matching address, phone number, etc.
The problem is that the database contains only numbers for the orders, however the program that contains the database which we use for inventory management prints our labels with the order number in the format TK123456. I need to figure out how to make SQL interpret the order number as just numbers when inputted, so basically cut the TK off the start.
SELECT RXFILL.RXFILL_ID, RXMAIN.RX_NUMBER, PATIENT.FIRSTNAME, PATIENT.LASTNAME,
SHIPADDRESS1, SHIPADDRESS2, SHIPCITY, SHIPSTATE, SHIPZIP, EMAIL
FROM RXFILL
LEFT JOIN RXMAIN ON RXFILL.RXMAIN_ID = RXMAIN.RXMAIN_ID
LEFT JOIN PATIENT ON RXMAIN.PATIENT_ID = PATIENT.PATIENT_ID
WHERE RXFILL_ID=$ORDERNUMBER
If I am understanding it correctly the $ORDERNUMBER is what needs to be adjusted to not include letters. However the program does specify the final line must be in the format WHERE [field name]=$ORDERNUMBER.
How can this be done?
If you only want this to be solved in SQL and not in the calling application, and you know that the first two characters of $ORDERNUMBER will always be 'TK', then you can easily solve it by taking a substring of $ORDERNUMBER starting at the third character... i.e.
WHERE RXFILL_ID=SUBSTRING($ORDERNUMBER, 2).
That syntax might not be exact, since you haven't divulged your DBMS type and each DBMS implements SUBSTRING in whatever way they want.
If you share more info about the calling application which sets $ORDERNUMBER, I'm sure it would be better to make the change there.
I have write a function for this.
CREATE Function SelectNumbersFromString(#str varchar(max))
Returns varchar(max) as
BEGIN
Declare #cchk char(5);
Declare #len int ;
Declare #aschr int;
SET #len = ( SElect len(#str) );
Declare #count int
SET #count = 1
DECLARE #ans varchar(max)
SET #ans = ''
While #count <= #len
BEGIN
SET #cchk = ( select Substring(#str,#count,1) );
SET #aschr = ( select ASCII(#cchk) );
IF #aschr in ( 49,50,51,52,53,54,55,56,57,58 )
BEGIN
SET #ans = #ans + CHAR(#aschr)
END
SET #count = #count + 1;
END
RETURN #ans;
END
TESTED
SELECT SelectNumbersFromString('abc3deef5ff6') will return 356
From http://wfjanjua.blogspot.com/2012/07/add-numbers-from-stringvarchar-in-tsql.html

How do I make a function in SQL Server that accepts a column of data?

I made the following function in SQL Server 2008 earlier this week that takes two parameters and uses them to select a column of "detail" records and returns them as a single varchar list of comma separated values. Now that I get to thinking about it, I would like to take this table and application-specific function and make it more generic.
I am not well-versed in defining SQL functions, as this is my first. How can I change this function to accept a single "column" worth of data, so that I can use it in a more generic way?
Instead of calling:
SELECT ejc_concatFormDetails(formuid, categoryName)
I would like to make it work like:
SELECT concatColumnValues(SELECT someColumn FROM SomeTable)
Here is my function definition:
FUNCTION [DNet].[ejc_concatFormDetails](#formuid AS int, #category as VARCHAR(75))
RETURNS VARCHAR(1000) AS
BEGIN
DECLARE #returnData VARCHAR(1000)
DECLARE #currentData VARCHAR(75)
DECLARE dataCursor CURSOR FAST_FORWARD FOR
SELECT data FROM DNet.ejc_FormDetails WHERE formuid = #formuid AND category = #category
SET #returnData = ''
OPEN dataCursor
FETCH NEXT FROM dataCursor INTO #currentData
WHILE (##FETCH_STATUS = 0)
BEGIN
SET #returnData = #returnData + ', ' + #currentData
FETCH NEXT FROM dataCursor INTO #currentData
END
CLOSE dataCursor
DEALLOCATE dataCursor
RETURN SUBSTRING(#returnData,3,1000)
END
As you can see, I am selecting the column data within my function and then looping over the results with a cursor to build my comma separated varchar.
How can I alter this to accept a single parameter that is a result set and then access that result set with a cursor?
Others have answered your main question - but let me point out another problem with your function - the terrible use of a CURSOR!
You can easily rewrite this function to use no cursor, no WHILE loop - nothing like that. It'll be tons faster, and a lot easier, too - much less code:
FUNCTION DNet.ejc_concatFormDetails
(#formuid AS int, #category as VARCHAR(75))
RETURNS VARCHAR(1000)
AS
RETURN
SUBSTRING(
(SELECT ', ' + data
FROM DNet.ejc_FormDetails
WHERE formuid = #formuid AND category = #category
FOR XML PATH('')
), 3, 1000)
The trick is to use the FOR XML PATH('') - this returns a concatenated list of your data columns and your fixed ', ' delimiters. Add a SUBSTRING() on that and you're done! As easy as that..... no dogged-slow CURSOR, no messie concatenation and all that gooey code - just one statement and that's all there is.
You can use table-valued parameters:
CREATE FUNCTION MyFunction(
#Data AS TABLE (
Column1 int,
Column2 nvarchar(50),
Column3 datetime
)
)
RETURNS NVARCHAR(MAX)
AS BEGIN
/* here you can do what you want */
END
You can use Table Valued Parameters as of SQL Server 2008, which would allow you to pass a TABLE variable in as a parameter. The limitations and examples for this are all in that linked article.
However, I'd also point out that using a cursor could well be painful for performance.
You don't need to use a cursor, as you can do it all in 1 SELECT statement:
SELECT #MyCSVString = COALESCE(#MyCSVString + ', ', '') + data
FROM DNet.ejc_FormDetails
WHERE formuid = #formuid AND category = #category
No need for a cursor
Your question is a bit unclear. In your first SQL statement it looks like you're trying to pass columns to the function, but there is no WHERE clause. In the second SQL statement you're passing a collection of rows (results from a SELECT). Can you supply some sample data and expected outcome?
Without fully understanding your goal, you could look into changing the parameter to be a table variable. Fill a table variable local to the calling code and pass that into the function. You could do that as a stored procedure though and wouldn't need a function.

Efficiently Handling Multiple Optional Constraints in Where Clause

This is somewhat of a sequel to Slow Exists Check. Alex's suggestion works and successfully avoids code repetition, but I still end up with a second issue. Consider the example below (From AlexKuznetsov). In it, I have two branches to handle 1 contraint. If I had 2 optional constraints, I would end up with 4 branches. Basically, the number of branches increases exponentially with the number of constraints.
On the other hand, if I use a Multi-Statement Table-valued function or otherwise use temporary tables, the SQL query optimizer is not able to assist me, so things become slow. I am somewhat distrustful of dynamic SQL (and I've heard it is slow, too).
Can anyone offer suggestions on how to add more constraints without adding lots of if statements?
Note: I have previously tried just chaining x is null or inpo = #inpo together, but this is very slow. Keep in mind that while the inpo = #inpo test can be handled via some sort of indexing black magic, the nullity test ends up being evaluated for every row in the table.
IF #inpo IS NULL BEGIN
SELECT a,b,c
FROM dbo.ReuseMyQuery(#i1)
ORDER BY c;
END ELSE BEGIN
SELECT a,b,c
FROM dbo.ReuseMyQuery(#i1)
WHERE inpo = #inpo
ORDER BY c;
END
Variation Two: 2 constraints:
IF #inpo IS NULL BEGIN
IF #inpo2 IS NULL BEGIN
SELECT a,b,c
FROM dbo.ReuseMyQuery(#i1)
ORDER BY c;
END ELSE BEGIN
SELECT a,b,c
FROM dbo.ReuseMyQuery(#i1)
WHERE inpo2 = #inpo2
ORDER BY c;
END
END ELSE BEGIN
IF #inpo2 IS NULL BEGIN
SELECT a,b,c
FROM dbo.ReuseMyQuery(#i1)
WHERE inpo = #inpo
ORDER BY c;
END ELSE BEGIN
SELECT a,b,c
FROM dbo.ReuseMyQuery(#i1)
WHERE inpo = #inpo AND
inpo2 = #inpo2
ORDER BY c;
END
END
this is the best reference: http://www.sommarskog.se/dyn-search-2005.html
In such cases I use sp_executesql as described in Erland's article: Using sp_executesql
Whenever dynamic SQL is used, missing permissions may be a problem, so I have a real network account for unit testing, I add that account to the actual role, and I impersonate with that real account whenever I test dynamic SQL, as described here: Database Unit Testing: Impersonation
Here's a rough example. Modify the LIKE statements in the WHERE clause depending if you want "starts with" or "contains" or an exact match in your query.
CREATE PROCEDURE dbo.test
#name AS VARCHAR(50) = NULL,
#address1 AS VARCHAR(50) = NULL,
#address2 AS VARCHAR(50) = NULL,
#city AS VARCHAR(50) = NULL,
#state AS VARCHAR(50) = NULL,
#zip_code AS VARCHAR(50) = NULL
AS
BEGIN
SELECT [name],
address1,
address2,
city,
state,
zip_code
FROM my_table
WHERE ([name] LIKE #name + '%' OR #name IS NULL)
AND (address1 LIKE #address1 + '%' OR #address1 IS NULL)
AND (address2 LIKE #address2 + '%' OR #address2 IS NULL)
AND (city LIKE #city + '%' OR #city IS NULL)
AND (state LIKE #state + '%' OR #state IS NULL)
AND (zip_code LIKE #zip_code + '%' OR #zip_code IS NULL)
ORDER BY [name]
END
GO
Select blah from foo
Where (#inpo1 is null or #inpo1 = inpo1)
and (#inpo2 is null or #inpo2 = inpo2)
Apparently this is too slow. Interesting.
Have you considered code generation? Lengthy queries with lots of duplication is only an issue if it has to be maintained directly.
I realise your question may be purely academic, but if you have real world use cases have you considered only providing optimised queries for the most common scenarios?