Best way to replace If-else in sql - sql

I have a search option in my website with 4 searching criteria and it is not compulsory to enter all of those to search . I want to implement this logic in my data layer more specifically my stored procedure. One such approach is
if ( all four are empty)
select command
else if ( 3 are empty
select command
and so on...
Is there any other way where i can replace these IF statements with some better logic. In short i would like the search to be made only based on the values provided by user

If you're using SQL Server 2008 or later, aside from very few unpatched installations at specific service packs and hotfix levels, this pattern will give you the best performance.
select ...
from ...
where (#criteria1 is null or column1 = #criteria1)
and (#criteria2 is null or column2 = #criteria2)
option (recompile)
Best reference for this type of dynamic criteria query writing is by Erland Sommarskog here.
If you can present only the filters requires by dynamically generating the query from the front-end, you'll achieve better results than this pattern for SQL Server 2005, although it's still the best option if dynamic SQL is not possible.

You can use case logic:
SELECT
CASE
WHEN all four are empty THEN expression
WHEN 3 are empty THEN expression
END
FROM ...
See details here:
http://msdn.microsoft.com/en-us/library/ms181765.aspx

Create base query and based on 4 input values append in where clause dynamically.

You might be looking for something as follows
Create sp datasearch
(
#param1 datatype=null,
#param2 datatype=null,
#param3 datatype=null,
#param4 datatype=null
)
as
select * from table where
col1=isnull(#param1 ,col1) and
col2=isnull(#param2 ,col2) and
col3=isnull(#param3 ,col3) and
col4=isnull(#param4 ,col4) and

In my experience, this kind of search also requires a ranking - a record where 4 of the criteria are matched is more important than a record where one is matched.
I haven't got access to a database right now, so the syntax below might be a bit broken, but in principle:
select ID, column1, column2, column3, column4, count(*) as rank
from
(select ID, column1, column2, column3, column4
from searchtable
where column1 = #criteria1
union
select ID, column1, column2, column3, column4
from searchtable
where column2 = #criteria2
union
select ID, column1, column2, column3, column4
from searchtable
where column3 = #criteria3
union
select ID, column1, column2, column3, column4
from searchtable
where column4 = #criteria4)
group by select ID, column1, column2, column3, column4
order by rank desc

Related

SQL: copy new values from one SQL table to another

I am trying to copy values from one SQL table to another. The problem I have is that some of the values already exists in both tables and I only want to copy unique values. The below code should show what I ideally want (but which doesn't work).
INSERT INTO newTable (column1, column2, column3,ID)
SELECT column1, column2, column3,ID
FROM oldTable
WHERE Newtable.ID <> oldTable.ID
Can someone present a solution that works? Thanks in advance.
If you are using SQL server 2008+ you can use an except statement:
INSERT INTO newTable
(
column1,
column2,
column3,
ID
)
SELECT column1,
column2,
column3,
ID
FROM oldTable
EXCEPT
SELECT column1,
column2,
column3,
ID
FROM newTable;
You can try to use NOT exists on where clause.
INSERT INTO newtable
(column1,
column2,
column3,
id)
SELECT column1,
column2,
column3,
id
FROM oldtable o
WHERE NOT EXISTS (SELECT 1
FROM newtable t
WHERE t.id = o.id);
SQL-Server SQLFIDDLE
My-sql SQLFIDDLE
Oracle SQLFIDDLE
It really depends on the number of records you're dealing with.
This guy has benchmarked all the different ways of doing it. Which way is "better" depends on the number of records.
Also, if this is an ongoing process, you'll be better off with an INSERT trigger so you can just add the new records when they happen instead of needing to query the whole table every time.

Return distinct values from multiple columns in one query

i have searched but i did not find any good answer actually i got the distinct value but the problem is i am applying query on two columns it should return distinct values but it is returning these values
Au |FAA303
Au |FAA505
From my table i want to appear Au only one time as it is now associated with the FAA303 and FAA505
What i want is like this
Au |FAA303
|FAA505
This is my query in postgresql. I am kinda new to the database queries.
select distinct column1, column2
from table_name
The distinct keyword applies to the combination of all selected fields, not to the first one only.
Suppressing repeated values is something you would typically do in an application that connects to your database and performs the query.
Just to show you that it is possible in SQL, I provide you this query, but please consider doing this in the application instead:
select case row_number() over (partition by column1 order by column2)
when 1 then column1
end as column1,
column2
from (
select distinct column1,
column2
from table_name
order by column1, column2
)

Is it possible to ORDER BY a computed column without including it in the result set?

I have this query:
SELECT Column1, Column2, Column3, /* computed column */ AS SortColumn
FROM Table1
ORDER BY SortColumn
SortColumn serves no other purpose as to define an order for sorting the result set. Thus I'd like to omit it in the result set to decrease the size of the data sent to the client. The following fails …
SELECT Column1, Column2, Column3
FROM (
SELECT Column1, Column2, Column3, /* computed column */ AS SortColumn
FROM Table1
ORDER BY SortColumn
) AS SortedTable1
… because of:
Msg 1033, Level 15, State 1
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.
So there's this hacky solution:
SELECT Column1, Column2, Column3
FROM (
SELECT TOP /* very high number */ Column1, Column2, Column3, /* computed column */ AS SortColumn
FROM Table1
ORDER BY SortColumn
) AS SortedTable1
Is there a clean solution I'm not aware of, since this doesn't sound like a rare scenario?
Edit:
The solutions already given work indeed fine for the query I referred to. Unfortunately, I left out an important detail: The (already existent) query consists of two SELECTs with a UNION in between, which changes the matter pretty much (again simplified, and hopefully not too simplified):
SELECT Column1, Column2, Column3
FROM Table1
UNION ALL
SELECT Column1, Column2, Column3
FROM Table1
ORDER BY /* computed column */
Msg 104, Level 16, State 1
ORDER BY items must appear in the select list if the statement contains a UNION, INTERSECT or EXCEPT operator.
So this error message clearly says that I have to put the computed column in both of the select lists. So there we are again with the subquery solution which doesn't reliably work, as pointed out in the answers.
You don't need to have a computed column in the select statement to use it in an order by
SELECT Column1, Column2, Column3
FROM Table1
ORDER BY /* computed column */
If you need to do it using UNION, then do the UNION in a cte, and the order by in the select, making sure to include all the columns you need to do the calculation in the CTE
WITH src AS (
SELECT Column1, Column2, Column3, /* computation */ ColumnNeededForOrderBy
FROM Table1
UNION ALL
SELECT Column1, Column2, Column3, /* computation */ ColumnNeededForOrderBy
FROM Table2
)
SELECT Column1, Column2, Column3
FROM src
ORDER BY ColumnNeededForOrderBy
If you don't care to be specific with the column name, you can use the column index and skip the CTE. I don't like this because you might add a column to the query later and forget to update the index in the ORDER BY clause (I've done it before). Also, the query plans will likely be the same, so it's not like the CTE will cost you anything.
SELECT Column1, Column2, Column3, /* computation */
FROM Table1
UNION ALL
SELECT Column1, Column2, Column3, /* computation */
FROM Table2
ORDER BY 4
If, for whatever reason, it's not practical to do the calculation in the ORDER BY, you can do something quite similar to your attempt:
SELECT Column1, Column2, Column3
FROM (
SELECT Column1, Column2, Column3, /* computed column */ AS SortColumn
FROM Table1
) AS SortedTable1
ORDER BY SortColumn
Note that all that's changed here is that the ORDER BY is applied to the outer query. It's perfectly valid to reference columns in the ORDER BY that don't appear in the SELECT clause.
Just put the expression in the order by:
SELECT Column1, Column2, Column3,
FROM Table1
ORDER BY <computed column>
The reason this is forbidden is that the ordering of the outer select has nothing to do with the ordering of the inner select - not by contract. So if you use order by without a top clause, you're obviously making a mistake. By using top the way you do, you simply hide the error, but you still have the same mistake.
Your hack only works because the engine happened to preserve the order - but that's not a given, and there's no way to enforce that (other than using order by in the outer query). For example, a different index usage or parallel execution can scramble your data.
So no, there isn't another way - you need to order by in the outer query, and that requires you to output the column you want to sort by in the subquery. And unless you're using *, it's not like it makes any difference - you don't need to select it in the outer select, just the inner one. And only the outer select is sent to the client :)
The only place for an ORDER BY is the outer most statement.
Of course there are exceptions: If you for example need the TOP record for a filtered list (e.g. the last valid value on a given date). But in these cases you must combine ORDER BY with TOP.
Only the outer most ORDER BY will sort the list you get.
After the edit looks like this is what you need
SELECT Column1, Column2, Column3
FROM
(
SELECT Column1, Column2, Column3
FROM Table1
UNION ALL
SELECT Column1, Column2, Column3
FROM Table1
)
ORDER BY /* computed column */

how to do nested SQL select count

i'm querying a system that won't allow using DISTINCT, so my alternative is to do a GROUP BY to get near to a result
my desired query was meant to look like this,
SELECT
SUM(column1) AS column1,
SUM(column2) AS column2,
COUNT(DISTINCT(column3)) AS column3
FROM table
for the alternative, i would think i'd need some type of nested query along the lines of this,
SELECT
SUM(column1) AS column1,
SUM(column2) AS column2,
COUNT(SELECT column FROM table GROUP BY column) AS column3
FROM table
but it didn't work. Am i close?
You are using the wrong syntax for COUNT(DISTINCT). The DISTINCT part is a keyword, not a function. Based on the docs, this ought to work:
SELECT
SUM(column1) AS column1,
SUM(column2) AS column2,
COUNT(DISTINCT column3) AS column3
FROM table
Do, however, read the docs. BigQuery's implementation of COUNT(DISTINCT) is a bit unusual, apparently so as to scale better for big data. If you are trying to count a large number of distinct values then you may need to specify a second parameter (and you have an inherent scaling problem).
Update:
If you have a large number of distinct column3 values to count, and you want an exact count, then perhaps you can perform a join instead of putting a subquery in the select list (which BigQuery seems not to permit):
SELECT *
FROM (
SELECT
SUM(column1) AS column1,
SUM(column2) AS column2
FROM table
)
CROSS JOIN (
SELECT count(*) AS column3
FROM (
SELECT column3
FROM table
GROUP BY column3
)
)
Update 2:
Not that joining two one-row tables would be at all expensive, but #FelipeHoffa got me thinking more about this, and I realized I had missed a simpler solution:
SELECT
SUM(column1) AS column1,
SUM(column2) AS column2,
COUNT(*) AS column3
FROM (
SELECT
SUM(column1) AS column1,
SUM(column2) AS column2
FROM table
GROUP BY column3
)
This one computes a subtotal of column1 and column2 values, grouping by column3, then counts and totals all the subtotal rows. It feels right.
FWIW, the way you are trying to use DISTINCT isn't how its normally used, as its meant to show unique rows, not unique values for one column in a dataset. GROUP BY is more in line with what I believe you are ultimately trying to accomplish.
Depending upon what you need you could do one of a couple things. Using your second query, you would need to modify your subquery to get a count, not the actual values, like:
SELECT
SUM(column1) AS column1,
SUM(column2) AS column2,
(SELECT sum(1) FROM table GROUP BY column) AS column3
FROM table
Alternatively, you could do a query off your initial query, something like this:
SELECT sum(column1), sum(column2), sum(column4) from (
SELECT
SUM(column1) AS column1,
SUM(column2) AS column2,
1 AS column4
FROM table GROUP BY column3)
GROUP BY column4
Edit: The above is generic SQL, not too familiar with Google Big Query
You can probably use a CTE
WITH result as (select column from table group by column)
SELECT
SUM(column1) AS column1,
SUM(column2) AS column2,
Select Count(*) From result AS column3
FROM table
Instead of doing a COUNT(DISTINCT), you can get the same results by running a GROUP BY first, and then counting results.
For example, the number of different words that Shakespeare used by year:
SELECT corpus_date, COUNT(word) different_words
FROM (
SELECT word, corpus_date
FROM [publicdata:samples.shakespeare]
GROUP BY word, corpus_date
)
GROUP BY corpus_date
ORDER BY corpus_date
As a bonus, let's add a column that identifies which books were written during each year:
SELECT corpus_date, COUNT(word) different_words, GROUP_CONCAT(UNIQUE(corpus)) books
FROM (
SELECT word, corpus_date, UNIQUE(corpus) corpus
FROM [publicdata:samples.shakespeare]
GROUP BY word, corpus_date
)
GROUP BY corpus_date
ORDER BY corpus_date

using two different order by statements in one query

Consider a column1 in a table has values 'A' or 'B' or null. if the Column1 has value 'A', a select query needs to be executed ordered by column2, else the select query needs to be executed ordered by column3.
Can you please help me to acheive this requirement via single query.
Just a guess, have to be checked!
select *, decode (column1, 'A', column2, column3) as field_for_order
from your_table
order by field_for_order