How to concatenate row values for use in WHERE clause of T-SQL query - sql

I want to write a query in T-SQL to perform a search on two concatenated columns. The two columns are fname and lname. Here is what I have so far:
SELECT
fname,
lname,
...
FROM
users
JOIN
othertable ON foo=bar
WHERE
fname+' '+lname LIKE '%query%'
SQL server doesn't like that syntax, though. How do I structure the query so that I can perform a WHERE LIKE operation that searches through two concatenated columns, allowing me to search the user's full name, rather than just first name and last name individually?

I can only suggest that one of fname or lname is NULL so the LIKE fails., (NULL concat anything is null)
Try
...
ISNULL(fname, '') + ' ' + ISNULL(lname, '') LIKE '%query%'
However, I would use a computed column and consider indexing it because this will run awfully.

My suggestion is to add a calculated column to your table for full_name
calculated column examples:
--drop table #test
create table #test (test varchar (10) , test2 varchar (5),[Calc] AS right(test, 3))
Insert #test
values('hello', 'Bye')
Insert #test
values('hello-bye', null)
Alter table #test
add [MyComputedColumn] AS substring(test,charindex('-',test), len(test)),
Concatenatedcolum as test+ ' ' +test2
select * from #test
As you can see you may have to play around a bit until you get the results you want. Do that in a temp table first to avoid having to restructure the database table multiple times. For names, especially if you are using middle name which is often blank, you may need to add some code to handle nulls. You may also need to have code sometimes to cast to the same datatype if one filed you are concatenating is an int for instance and the other a varchar.

I think one of the join conditions might be causing a problem. Try rewriting it, you may find the error goes away ;)

Related

Why won't SQL return a value if I use "=" but will return if I use "like"?

Here are my queries:
(Won't return a value)
select * from T_VoucherHeaderEntry
where Vhe_VoucherNo = 'APV-1808-00160'
(Will return a value)
Select * from T_VoucherHeaderEntry where Vhe_VoucherNo like 'APV-1808-00160%'
I tried trimming my first query but it doesn't work.
You appear to have other control characters in your stored data, specifically carriage-return and line-feed. This highlights the issue and the final query finds all rows currently affected by this1:
;declare #t table (Val1 varchar(20))
insert into #t(Val1) values ('abc
'),('def')
select * from #t where Val1 = 'abc'
select * from #t where Val1 like 'abc%'
select * from #t where Val1 like '%
%'
So, fix those rows however you choose to do so. Next, add a CHECK constraint on this column:
ALTER TABLE T_VoucherHeaderEntry
ADD CONSTRAINT CK_T_VoucherHeaderEntry_NoExoticChars
CHECK (Vhe_VoucherNo not like '%[^-A-Za-z0-9]%')
(It's expressed as a double negative to say we want to disallow any character in the provided range. We have to put - as the first character so that it's interpreted literally and not as a range separator)
And finally update your applications to not attempt to insert such bogus data in the first place.
1The third query identifies those specifically affected by CR/LF issue. For a more general approach, once you've decided on the appropriate character range to specify in your check constraint, a variant of that same approach will find rows that won't satisfy the check constraint for you to fix.
if your Vde_VoucherNo column contain this 'APV-1808-00160' value then definitely below should work and return data
select * from T_VoucherHeaderEntry
where Vhe_VoucherNo = 'APV-1808-00160'
in case of white-space in your column value, you can use trim function
select * from T_VoucherHeaderEntry
where trim(Vhe_VoucherNo) = 'APV-1808-00160'
But if your column contain pattern of this values APV-1808-00160 then like will work which is your 2nd query
Select * from T_VoucherHeaderEntry
where Vhe_VoucherNo like 'APV-1808-00160%'
BTW noticed the two query is from two different table , so that may be also reason

SQL Server stored procedure to search list of values without special characters

What is the most efficient way to search a column and return all matching values while ignoring special characters?
For example if a table has a part_number column with the following values '10-01' '14-02-65' '345-23423' and the user searches for '10_01' and 140265 it should return '10-01' and '14-02-65'.
Processing the input to with a regex to remove those characters is possible, so the stored procedure could could be passed a parameter '1001 140265' then it could split that input to form a SQL statement like
SELECT *
FROM MyTable
WHERE part_number IN ('1001', '140265')
The problem here is that this will not match anything. In this case the following would work
SELECT *
FROM MyTable
WHERE REPLACE(part_number,'-','') IN ('1001', '140265')
But I need to remove all special characters. Or at the very least all of these characters ~!##$%^&*()_+?/\{}[]; with a replace for each of those characters the query takes several minutes when the number of parts in the IN clause is less than 200.
Performance is improved by creating a function that does the replaces, so the query takes less than a minute. But without removals the query takes around 1 second, is there any way to create some kind of functional index that will work on multiple SQL Server engines?
You could use a computed column and index it:
CREATE TABLE MyTable (
part_number VARCHAR(10) NOT NULL,
part_number_int AS CAST(replace(part_number, '-', '') AS int)
);
ALTER TABLE dbo.MyTable ADD PRIMARY KEY (part_number);
ALTER TABLE dbo.MyTable ADD UNIQUE (part_number_int);
INSERT INTO dbo.MyTable (part_number)
VALUES ('100-1'), ('140265');
SELECT *
FROM dbo.MyTable AS MT
WHERE MT.part_number_int IN ('1001', '140265');
Of course your replace statement will be more complex and you'll have to sanitize user input the same way you sanitize column values. But this is going to be the most efficient way to do it.
This query can now seek your column efficiently:
But to be honest, I'd just create a separate column to store cleansed values for querying purpose and keep the actual values for display. You'll have to take care of extra update/insert clauses, but that's a minimum damage.

Matching sub string in a column

First I apologize for the poor formatting here.
Second I should say up front that changing the table schema is not an option.
So I have a table defined as follows:
Pin varchar
OfferCode varchar
Pin will contain data such as:
abc,
abc123
OfferCode will contain data such as:
123
123~124~125
I need a query to check for a count of a Pin/OfferCode combination and when I say OfferCode, I mean an individual item delimited by the tilde.
For example if there is one row that looks like abc, 123 and another that looks like abc,123~124, and I search for a count of Pin=abc,OfferCode=123 I wand to get a count = 2.
Obviously I can do a similar query to this:
SELECT count(1) from MyTable (nolock) where OfferCode like '%' + #OfferCode + '%' and Pin = #Pin
using like here is very expensive and I'm hoping there may be a more efficient way.
I'm also looking into using a split string solution. I have a Table-valued function SplitString(string,delim) that will return table OutParam, but I'm not quite sure how to apply this to a table column vs a string. Would this even be worth wile pursuing? It seems like it would be much more expensive, but I'm unable to get a working solution to compare to the like solution.
Your like/% solution is open to a bug if you had offer codes other than 3 digits (if there was offer code 123 and 1234, searching for like '%123%' would return both, which is wrong). You can use your string function this way:
SELECT Pin, count(1)
FROM MyTable (nolock)
CROSS APPLY SplitString(OfferCode,'~') OutParam
WHERE OutParam.Value = #OfferCode and Pin = #Pin
GROUP BY Pin
If you have a relatively small table you can probably get away with this. If you are working with a large number of rows or encountering performance problems, it would be more effective to normalize it as RedFilter suggested.
using like here is very expensive and I'm hoping there may be a more efficient way
The efficient way is to normalize the schema and put each OfferCode in its own row.
Then your query is more like (although you may need to use an intersection table depending on your schema):
select count(*)
from MyTable
where OfferCode = #OfferCode
and Pin = #Pin
Here is one way to use like for this problem, which is standard for getting exact matches when searching delimited strings while avoiding the '%123%' matches '123' and '1234' problem:
-- Create some test data
declare #table table (
Pin varchar(10) not null
, OfferCode varchar(100) not null
)
insert into #table select 'abc', '123'
insert into #table select 'abc', '123~124'
-- Mock some proc params
declare #Pin varchar(10) = 'abc'
declare #OfferCode varchar(10) = '123'
-- Run the actual query
select count(*) as Matches
from #table
where Pin = #Pin
-- Append delimiters to find exact matches
and '~' + OfferCode + '~' like '%~' + #OfferCode + '~%'
As you can see, we're adding the delimiters to the searched string, and also the search string in order to find matches, thus avoiding the bugs mentioned by other answers.
I highly doubt that a string splitting function will yield better performance over like, but it may be worth a test or two using some of the more recently suggested methods. If you still have unacceptable performance, you have a few options:
Updated:
Try an index on OfferCode (or on a computed persisted column of '~' + OfferCode + '~'). Contrary to the myth that SQL Server won't use an index with like and wildcards, this might actually help.
Check out full text search.
Create a normalized version of this table using a string splitter. Use this table to run your counts. Update this table according to some schedule or event (trigger, etc.).
If you have some standard search terms, pre-calculate the counts for these and store them on some regular basis.
Actually, the LIKE condition is going to have much less cost than doing any sort of string manipulation and comparison.
http://www.simple-talk.com/sql/performance/the-seven-sins-against-tsql-performance/

Forcing a datatype in MS Access make table query

I have a query in MS Access which creates a table from two subqueries. For two of the columns being created, I'm dividing one column from the first subquery into a column from the second subquery.
The datatype of the first column is a double; the datatype of the second column is decimal, with scale of 2, but I want the second column to be a double as well.
Is there a way to force the datatype when creating a table through a standard make-table Access query?
One way to do it is to explicitly create the table before putting anything into it.
Your current statement is probably like this:
SELECT Persons.LastName,Orders.OrderNo
INTO Persons_Order_Backup
FROM Persons
INNER JOIN Orders
ON Persons.P_Id=Orders.P_Id
WHERE FirstName = 'Alistair'
But you can also do this:
----Create NewTable
CREATE TABLE NewTable(FirstName VARCHAR(100), LastName VARCHAR(100), Total DOUBLE)
----INSERT INTO NewTableusing SELECT
INSERT INTO NewTable(FirstName, LastName, Total)
SELECT FirstName, LastName,
FROM Person p
INNER JOIN Orders o
ON p.P_Id = o.P_Id
WHERE p.FirstName = 'Alistair'
This way you have total control over the column types. You can always drop the table later if you need to recreate it.
You can use the cast to FLOAT function CDBL() but, somewhat bizarrely, the Access Database Engine cannot handle the NULL value, so you must handle this yourself e.g.
SELECT first_column,
IIF(second_column IS NULL, NULL, CDBL(second_column))
AS second_column_as_float
INTO Table666
FROM MyTest;
...but you're going to need to ALTER TABLE to add your keys, constraints, etc. Better to simply CREATE TABLE first then use INSERT INTO..SELECT to populate it.
You can use CDbl around the columns.
An easy way to do this is to create an empty table with the correct field types and then to an Append-To query and Access will automatically convert the data to the destination field.
I had a similar situation, but I had a make-table query creating a field with NUMERIC datatype that I wanted to be short text.
What I did (and I got the idea from Stack) is to create the table with the field in question as Short Text, and at the same time build a delete query to scrub the records. I think it's funny that a DELETE query in access doesn't delete the table, just the records in it - I guess you have to use a DROP TABLE function for that, to purge a table...
Then, I converted my make-table query to an APPEND query, which I'd never done before... and I just added the running of the DELETE query to my process.
Thank you, Stack Overflow !
Steve
I add a '& ""' to the field I want to make sure are stored as text, and a ' *1 ' (as in multiplying the amount by 1) to the fields I want to store as numeric.
Seems to do the trick.
To get an Access query to create a table with three numeric output fields from input numeric fields, (it kept wanting to make the output fields text fields), had to combine several of the above suggestions. Pre-establish an empty output table with pre-defined output fields as integer, double and double. In the append query itself, multiply the numeric fields by one. It worked. Finally.

Handling missing columns in MySQL

Working on parsing a bunch of databases put together in an older, more freewheeling time into a new schema. Basically it's one database per year, with database names like foo98, foo99, foo2000, etc.
So for the most recent foo data, I can do something like
SELECT foo_person.mdname AS middle_name,
...
FROM foo_person, foo_place, foo_thing
As you get back into earlier versions of the foo database, middle name isn't stored. I've tried to build a kind of universal query, something like:
SELECT IFNULL(foo_person.mdname, "") AS middle_name,
...
FROM foo_person, foo_place, foo_thing
but MySQL complains about unknown column foo_person.mdname, which is entirely reasonable as it doesn't exist.
Is there some way to handle non-existent columns with just MySQL syntax, or will I have to write database-specific import queries?
There isn't any way of handling a non-existent column in sql (as opposed to an empty column).
You can tell whether the column is there or not using the information_schema tables, like so:
select * from information_schema.columns
where table_name='mytable' and table_schema='mydatabase';
Yes there is a way.
Let's consider these databases
DB2009 has Person with Fname, MInitial and LName
DB2008 has Person with Fname and LName
DB2007 has Person with PersonName
You can do something similar the following (I wrote this for MS SQL Server)
/*all three columns exist*/
SELECT FName, MInitial, LName
From DB2009.Person
UNION
/*one column is a forced null */
SELECT FName, NULL as MInitial, LName
From DB2008.Person
UNION
/*two columns are derived and one column is a forced null */
SELECT SubString (1, CharIndex (PersonName, ' '), PersonName) as FirstName,
NULL as MInitial,
SubString (CharIndex (PersonName, ' '), len (PersonName), PersonName),
From DB2007.Person
Could you rename the tables and create views in their place with the missing columns?
Not sure if this is what you're looking for, but thought I would suggest it.
-- Here is your original table
create table t (fname varchar(30), lname varchar(30));
-- Rename it to something else
alter table t rename to tOrig;
-- Create a view with the columns its missing that you need
create view t as select fname, lname, '' as mname from tOrig;
If you're working with heterogenous databases, I would use database-specific import queries anyways. You might need to join some columns and remove some, and truncate some, etc.
Instead of making more complex sql queries, perhaps it would be better to
alter the foo98 and foo99 tables to add in the missing columns.
For example, to add a column of type varchar(30) called "mdname" to foo98:
ALTER TABLE foo98 ADD mdname varchar(30) AFTER first_name;
Then you can relax and use the same simple SQL query no matter which tabel is being accessed.