faster way to do this select query - sql

Say Employee has three columns
FirstName, LastName and ID.
The query should first search for a name in firstname, only if its not found search for last name.
so select *from Employee where FirstName = 'test%' or lastname='test'%'. wont' work.
The query below will work.
select FirstName, LastName, ID
from EMPLOYEE
WHERE
LastName = 'Test%'
AND
(COUNT(SELECT FirstName, LastName, ID FROM EMPLOYEE WHERE FirstName = 'Test%')=0)
OR
SELECT FirstName, LastName, ID FROM EMPLOYEE WHERE FirstName = 'Test%'
I need to map this back to NHibernate, is there a faster efficient way of doing this instead of making two database calls?

Give this one a try:
SELECT FirstName, LastName, ID FROM EMPLOYEE WHERE FirstName = 'Test%'
OR (FirstName <> 'Test%' AND LastName = 'Test%')

FROM Employee
WHERE (FirstName LIKE 'Test%' AND LastName NOT LIKE 'Test%')
OR (LastName LIKE 'Test%' AND firstName NOT LIKE 'Test%')
Granted you don't care what order they come back in. If the records must come back with the records that the first name matches, followed by the names where the last name match, then this won't work.

"Premature optimization is the root of all evil".
Why would you care of how the search is done as the optimizer sees it, instead of declaring what you want?
It all boils down to a boolean truth table: when FirstName matches, you want the record (whatever is in LastName, match or nomatch) and if FirstName does not match, you want the record when LastName matches:
F match, L match => yes
F match, L nomatch => yes
F nomatch, L match => yes
F nomatch, L nomatch => no
That is exactly the OR condition: (FirstName matching) OR (LastName matching); the only discarded records are when both FirstName and LastName do not match.
The boolean optimization will ensure that the 2nd condition is not even evaluated when the 1st one is true.
So your query is:
SELECT FirstName, LastName, ID
FROM Employee
WHERE (FirstName LIKE 'Test%')
OR (LastName LIKE 'Test%')
UPDATE: I may have misunderstood the goal if it is indeed not to return any LastName match if records were found with only the FirstName...
Anyway, the stance on premature optimization is still valid...
You need somehow a 2 pass query as you cannot tell if LastName is to be considered until you're sure you don't have any match on FirstName. But with proper indexes and statistics, the optimizer will make it very efficient.
The query:
SELECT FirstName, LastName, ID
FROM Employee
WHERE (FirstName LIKE 'Test%')
OR (LastName LIKE 'Test%'
AND (SELECT Count(ID)
FROM Employee
WHERE FirstName LIKE 'Test%') = 0)
is only marginally more expensive than the previous.

Select *
From Employee
Where FirstName Like #Text + '%'
Union
Select *
From Employee
Where LastName Like #Text + '%';

I don't think any of these queries answers the question. My understanding is that a search on 'John%' should return employees with last name John, Johnson, etc. ONLY if there were no employees with first name John, Johnny, etc. All the queries show will return both John Adams and Lyndon Johnson if the table contains both, but only John Adams should appear, because last names should be matched ONLY if there are no first names that matched.
Here's a proposal using SQL Server syntax. It should be possible to write this in other dialects of SQL:
select top (1) with ties
FirstName, LastName, ID
from (
select
0 as SearchLastNames,
FirstName, LastName, ID
from EMPLOYEE
where FirstName like 'Test%'
union all
select
1 as SearchLastNames,
FirstName, LastName, ID
from EMPLOYEE
where LastName like 'Test%'
) as T
order by SearchLastNames;
If there are any matching first names, the smallest SearchLastNames value will be 0, and the TOP (1) with ties .. order by SearchLastNames will return information only for the first name matches (where SearchLastNames is 0).
If there are no matching first names, the only SearchLastNames value will be 1. In that case, TOP will return information for all last name matches (where SearchLastNames is 1), if there are any.
A more clumsy, but more portable solution is this:
select
FirstName, LastName, ID
from EMPLOYEE
where FirstName like 'Test%'
union all
select
FirstName, LastName, ID
from EMPLOYEE
where LastName like 'Test%'
and not exists (
select
FirstName, LastName, ID
from EMPLOYEE
where FirstName like 'Test%'
);

Related

How to exclude the column if all the values in column are null in SQL Server?

Let's say I have a table Employee which has columns FirstName, LastName, Address and Position.
Now when select * from Employee runs, the result is displayed as follows:
We can see that the value of Address is NULL for all the rows.
If such a condition occurs, I need only three columns FirstName, LastName and Position to be displayed.
Similarly, if the Position column is null for all the values then I want to exclude the Position column and display only FirstName and LastName.
How can I do that?
This can be done using
IF EXISTS (SELECT * FROM EMPLOYEE WHERE Address IS NOT NULL)
BEGIN
SELECT * FROM EMPLOYEE
END
ELSE
BEGIN
SELECT FirstName,Lastame,Position FROM EMPLOYEE
END

PostgreSQL: `VIEW` returns no rows with `SPLIT_PART()` columns

Problem Description:
Hi everyone, I'm having some troubles querying on a VIEW whose columns are, in part, the result of SPLIT_PART() function on og table's column; I created the VIEW as it follows:
CREATE VIEW ClientsAddressList(Client_ID, FirstName, LastName, ResidenceAddress, City, PostalCode, Province) AS
SELECT Client_ID,
FirstName,
LastName,
SPLIT_PART(Address, '-', 1) AS ResidenceAddress,
SPLIT_PART(Address, '-', 2) AS City,
SPLIT_PART(Address, '-', 3) AS PostalCode,
SPLIT_PART(Address, '-', 4) AS Province
FROM Clients;
My intention was to divide the structured attribute (Clients.Address defined as a string VARCHAR(255)) which contains all the informations releated to client's domicile in several columns to separately query (e.g. SELECT FirstName, LastName FROM ClientAddressList WHERE City LIKE 'N%'; or SELECT Client_ID FROM ClientAddressList WHERE PostalCode = '82305';).
What I experience:
The Clients table contains one test row:
Client_ID
FirstName
LastName
ResidenceAddress
City
PostalCode
Province
00451
Ezio
Auditore
Via dei Banchi 45 - Florence - 50123 - Florence
Florence
50123
Florence
So my VIEW has this row:
Client_ID
FirstName
LastName
ResidenceAddress
City
PostalCode
Province
00451
Ezio
Auditore
Via dei Banchi 45
Florence
50123
Florence
I've tried:
SELECT Client_ID, FirstName, LastName
FROM ClientsAddressList
WHERE City = 'Florence'
And it returns no result:
Client_ID
FirstName
LastName
ResidenceAddress
City
PostalCode
Province
But if I query on columns that are not the result of SPLIT_PART() it works:
SELECT Client_ID, FirstName, LastName, City
FROM ClientsAddressList
WHERE Client_ID = '00451'
Client_ID
FirstName
LastName
City
00451
Ezio
Auditore
Florence
What I expect:
I would WHERE clause to work and returns values even on SPLIT_PART() result columns:
SELECT Client_ID
FROM ClientAddressList
WHERE PostalCode LIKE = '%123'
Client_ID
00451
Can someone explain me what could be the problem, please? Thank you so much!
As sticky bit wrote: there are spaces around the values. There are two ways to deal with this. One way is to just slap a trim() around the expressions in the view:
trim(SPLIT_PART(Address, '-', 2)) AS City,
The other option is to use an appropriate regex to split the information to remove the whitespace during splitting:
select client_id,
firstname,
lastname,
address[1] as residenceaddress,
address[2] as city,
address[3] as postalcode,
address[4] as province
from (
select client_id, firstname, lastname,
regexp_split_to_array(residenceaddress, '\s*-\s*') as address
from clients
) t
Online example
In the long run you should fix your data model by properly normalizing it and storing those values in separate columns. I don't know how many city names contain dashes in Italy, but in Germany, this pattern would break quickly with city names like "Garmisch-Partenkirchen" or "Leinfelden-Echterdingen"

SQL Join 2 tables with different columns

I am trying to create a single login function for usernames and password that exist across 2 tables.
Currently I have
SELECT * FROM CUSTOMER WHERE username = usernameIN AND password = passwordIN
SELECT * FROM STAFF WHERE username = usernameIN AND password = passwordIN
If there is a match ill log the user in.
Is there a query to join these 2 tables so I can run 1 SQL query on them.
Input tables
CUSTOMER
username,password,firstname,lastname,addressline1,town
andys,pass123,andy,smith,123 fake st,fake town
STAFF
username,password,firstname,lastname,position,salary
davidq,pass321,david,queen,manager,10000
Expected Output from SQL Query
OUTPUT TABLE
username,password,firstname,lastname,addressline1,town,position,salary
andys,pass123,andy,smith,123 fake st,fake town,null,null
davidq,pass321,david,queen,null,null,manager,10000
You need UNION ALL.
SELECT username,
password1,
firstname,
lastname,
addressline1,
town,
NULL AS manager,
NULL AS salary
FROM customer
UNION ALL
SELECT username,
password1,
firstname,
lastname,
NULL,
NULL,
POSITION,
salary
FROM staff;
You can add WHERE clauses to your individual queries accordingly.
More over if you want to avoid duplicate rows, use UNION
Result:
username password1 firstname lastname addressline1 town manager salary
--------------------------------------------------------------------------------------------------
andys pass123 andy smith 123 fake st fake town NULL NULL
davidq pass321 david queen NULL NULL manager 10000
DEMO
SELECT
username,password,firstname,lastname,addressline1,town, null as position, null as salary
FROM
CUSTOMER
WHERE username = usernameIN AND password = passwordIN
UNION ALL
SELECT
username,password,firstname,lastname, null, null, position,salary
FROM
STAFF
WHERE username = usernameIN AND password = passwordIN
You'll have to add aliases
You can join both the table using UNION like below.
SELECT username, password, firstname, lastname, addressline1, town, position, salary FROM
(SELECT username, password, firstname, lastname, addressline1, town, null position, null salary FROM CUSTOMER
UNION ALL
SELECT username, password, firstname, lastname, null addressline1, null town, position,salary FROM STAFF) AS t
WHERE t.username = 'username' AND t.password = 'password'
Use UNION
SELECT column_name(s) FROM table1
UNION
SELECT column_name(s) FROM table2;

select distinct rows based on column values

In one table i have four columns 'FirstName' , 'LastName' , 'Dob', 'Number'
There are multiple rows for the unique 'first Name' and 'LastName' and they can have same or different 'Number' and 'Dob' values
FirstName LastName Dob Number
Alice Smith 02/03/77 0876543 (require)
Alice Smith Null 0876543 (Not require)
Alice Smith Null Null (Not require)
John Adam 11/28/63 3265783 (require)
John Adam 11/28/63 Null (Not Require)
John Adam 05/15/58 Null (require)
Sally Smith Null Null (require)
I want distinct rows, but I need only one record if either the 'Number'or the 'Dob' matches with other records of same FirstName' and 'LastName'
I am lookin for the rows that are labeled 'require' in the example above. The combinations i tried didn't get results i thought they would.
Thanks
I would first select all rows that have all four fields populated:
SELECT DISTINCT FirstName, LastName, Dob, Number
FROM customers
WHERE Dob IS NOT NULL And Number IS NOT NULL
Then I would add to the selection the records with NOT NULL values that do not exist in the first selection:
with RequiredSet as (
SELECT DISTINCT FirstName, LastName, Dob, Number
FROM customers
WHERE Dob IS NOT NULL And Number IS NOT NULL
), AdditionalSet as (
SELECT distinct c.FirstName, c.LastName, c.Dob, c.Number
FROM customers c
WHERE Dob IS NOT NULL And NOT EXISTS(SELECT 1
FROM RequiredSet r
WHERE r.FirstName = c.FirstName And r.LastName=c.LastName And r.Dob=c.Dob)
OR Number IS NOT NULL And NOT EXISTS(SELECT 1
FROM RequiredSet r
WHERE r.FirstName = c.FirstName And r.LastName=c.LastName And r.Number=c.Number)
)
SELECT FirstName, LastName, Dob, Number
FROM RequiredSet
UNION ALL
SELECT FirstName, LastName, Dob, Number
FROM AdditionalSet

How to add table column headings to sql select statement

I have a SQL select statement like this:
select FirstName, LastName, Age from People
This will return me something like a table:
Peter Smith 34
John Walker 46
Pat Benetar 57
What I want is to insert the column headings into the first row like:
First Name Last Name Age
=========== ========== ====
Peter Smith 34
John Walker 46
Pat Benetar 57
Can someone suggest how this could be achieved?
Could you maybe create a temporary table with the headings and append the data one to this?
Neither of the answers above will work, unless all your names come after "first" in sort order.
Select FirstName, LastName
from (
select Sorter = 1, FirstName, LastName from People
union all
select 0, 'FirstName', 'LastName') X
order by Sorter, FirstName -- or whatever ordering you need
If you want to do this to all non-varchar columns as well, the CONS are (at least):
ALL your data will become VARCHAR. If you use Visual Studio for example, you will NO LONGER be able to recognize or use date values. Or int values. Or any other for that matter.
You need to explicitly provide a format to datetime values like DOB. DOB values in Varchar in the format dd-mm-yyyy (if that is what you choose to turn them into) won't sort properly.
The SQL to achieve this, however not-recommended, is
Select FirstName, LastName, Age, DOB
from (
select Sorter = 1,
Convert(Varchar(max), FirstName) as FirstName,
Convert(Varchar(max), LastName) as LastName,
Convert(Varchar(max), Age) as Age,
Convert(Varchar(max), DOB, 126) as DOB
from People
union all
select 0, 'FirstName', 'LastName', 'Age', 'DOB') X
order by Sorter, FirstName -- or whatever ordering you need
The lightest-weight way to do this is probably to do a UNION:
SELECT 'FirstName' AS FirstName, 'LastName' AS LastName
UNION ALL
SELECT FirstName, LastName
FROM People
No need to create temporary tables.
The UNION All is the solution except it should be pointed out that:
To add a header to a non-char column will require converting the column in the first part of the query.
If the converted column is used as part of the sort order then the field reference will have to be to the name of the column in the query, not the table
example:
Select Convert(varchar(25), b.integerfiled) AS [Converted Field]...
... Order by [Converted Field]