How to ensure specific WHERE condition evaluates first on view - sql-server

Consider the following view:
create view x as
select 1 as [source],* from some_table
union all
select 2,* from some_other_table
When I run
select * from x where source=1
can I be sure that the select 2.... query is not even executed?
The reason is that in my case, that one is an openquery with sluggish speed, which I want to avoid.

You can do stuff like this, but i dont know if that meets your other requirements you might have - Otherwise you can create 2 views.
CREATE proc dbo.usp_selectspecificquery
#source int
AS
BEGIN
IF(#source = 1)
BEGIN
Select 1 as source, * from some_table
END
ELSE
Select 1 as source,* from some_table
union all
select 2 as source,* from some_other_table
END
exec dbo.usp_selectspecificquery 1

Related

Update table from 2 Selects from a different database with thousands of rows SQL

I have 2 Openquerys that are just simple selects from 2 tables. My objective is to populate a single table with data from those 2 queries that basically return the same thing but with different names.
Example
1st Warehouse 1
Select * From OpenQuery ('SELECT * FROM Warehouse1')
2nd Warehouse 2
Select * From OpenQuery ('SELECT * FROM Warehouse2')
There are thousands of rows that i need to update my SQL table. Problem is, this is very expensive if i use UNION, and my question is how can achieve this for best performance possible? Also this is data from an external database so i really can't change the queries
I have to update my main table with these queries only when user access the list that shows the data
EDIT.
I wasn't very clear but both tables return same type of column
| ID | Warehouse | Ticket | Item | Qty
One belongs to Warehouse 1, the other to Warehouse 2, both have different amount of rows.
You can use inner join with update for this you need to make table alias as shown below
UPDATE A
SET A.<ColName> = B.<ColName>
from Warehouse1 A
INNER JOIN Warehouse2 B
ON A.<Id> = B.<Id>
--where if required
But why you need to UNION?
You can simply insert 2 times under a transaction.
BEGIN TRY
BEGIN TRAN T1
INSERT into mytable
--select from openquery table 1
INSERT into mytable
--select from openquery table 2
COMMIT TRAN T1
END TRY
BEGIN CATCH
---handle error
ROLLBACK TRAN T1
END CATCH
For anyone with the same problem as me. Here is the solution i came up with that suits my problem better.
I save the open query on a view since I don't need to change anything or insert in my database at all
/*************************** Views ********************************/
GO
IF OBJECT_ID('viewx_POE', 'v') IS NOT NULL
DROP VIEW viewx_POE
GO
CREATE VIEW viewx_POE AS
SELECT ET0104 AS Armazem,
ET0109 AS Localizacao,
ET0102 AS Etiqueta,
ET0101 AS Artigo,
ET0103 AS Quantidade
FROM OpenQuery(MACPAC, 'SELECT FET001.ET0104, FET001.ET0109, FET001.ET0102, FET001.ET0101, FET001.ET0103
FROM AUTO.D805DATPOR.FET001 FET001
WHERE (FET001.ET0104=''POE'') AND (FET001.ET0105=''DIS'')');
/**************************************************************************/
GO
IF OBJECT_ID('viewx_CORRICA', 'v') IS NOT NULL
DROP VIEW viewx_CORRICA
GO
CREATE VIEW viewx_CORRICA AS
SELECT GHZORI AS Armazem,
GHNEMP AS Localizacao,
LBLBNB AS Etiqueta,
GHLIB5 AS Artigo,
LBQTYD AS Quantidade
FROM OpenQuery(MACPAC, 'SELECT GA160H.LBLBNB, GA160H.GHLIB5, GA160H.GHZORI, GA160H.GHNEMP, GA160M.LBQTYD
FROM D805DATPOR.GA160H GA160H, D805DATPOR.GA160M GA160M
WHERE GA160M.LBLBNB = GA160H.LBLBNB AND (GA160H.GHZORI=''CORRICA'' AND GA160H.GHCSTA=''DIS'')');
And then when needed I select the view depending on the user rank and return whatever i need from it
GO
IF OBJECT_ID('dbo.spx_SELECT_RandomLocalizacoes') IS NOT NULL
DROP PROCEDURE spx_SELECT_RandomLocalizacoes
GO
CREATE PROCEDURE spx_SELECT_RandomLocalizacoes
#LocalizacoesMax int,
#Armazem int
AS
BEGIN
SET NOCOUNT ON
DECLARE #Output int
IF ( #Armazem = 'POE' )
BEGIN
SELECT TOP(10) xa.IdArmazem, vpoe.Localizacao, vpoe.Etiqueta, vpoe.Artigo, vpoe.Quantidade
FROM viewx_POE vpoe
INNER JOIN xArmazem xa
ON vpoe.Armazem = xa.Armazem
ORDER BY NEWID()
END
ELSE IF ( #Armazem = 'CORRICA' )
BEGIN
SELECT TOP(#LocalizacoesMax) * FROM viewx_CORRICA ORDER BY NEWID()
END
END

How to batch toggle comment/uncomment certain rows in SQL Server?

Is there a way, within SSMS or via a third-party app, to select a batch of lines to toggle their commented status?
Background: in my query, there are two "search modes" that I want to alternate between.
To do this, I need to comment lines 19, 107, 108, 112, and uncomment line 232. Then I need to do the opposite to go back to the other "mode".
Rather than scrolling through the query, is there some way to automate this process?
Example:
1 --select distinct x.name from (
2 select name, dateofbirth, favouritecolour
3 from classmates
4 where dateofbirth between '01-Mar-1990' and '17-Apr-1995'
5 --)x
6 union all
7 --select distinct x.name from (
8 select name, relationship, location
9 from family
10 where relationship = 'uncle'
11 --)x
For full detail, I could have the query like this. If I just wanted the names, I would uncomment lines 1,5,7 and 11.
My real life example is spread across hundreds of lines, and would involve commenting and uncommenting as part of the same "transition"
A simple way to do this is to use a variable:
--Searchmode 0 = Path A
--Searchmode 1 = Path B
DECLARE #SearchMode int = 0 --Change this to change path
IF #SearchMode = 0
BEGIN
SELECT blah
FROM tableA
END
IF #SearchMode = 1
BEGIN
SELECT blah
FROM tableB
END
You could also make it a procedure:
CREATE PROCEDURE dbo.exampleProc #SearchMode int
AS
BEGIN
IF #SearchMode = 0
BEGIN
SELECT blah
FROM tableA
END
IF #SearchMode = 1
BEGIN
SELECT blah
FROM tableB
END
END
Then just execute it and feed in the parameter value like this:
EXEC dbo.exampleProc 0
EXEC dbo.exampleProc 1
Edit:
You could also have the repeated parts of the query always run, then the extra filters only run if #SearchMode = 1. Something like:
DECLARE #SearchMode int = 0
select name, dateofbirth, favouritecolour
into #temp
from classmates
where dateofbirth between '01-Mar-1990' and '17-Apr-1995'
union all
select name, relationship, location
from family
where relationship = 'uncle'
IF #SearchMode = 0
BEGIN
SELECT *
FROM #temp
END
IF #SearchMode = 1
BEGIN
SELECT DISTINCT Name
FROM #temp
END

Stored procedure with conditional Where clause in SQL Server

I am creating a SQL Server stored procedure. It's a simple SELECT query that I am building. One of the parameters is to look for a flag parameter. If that parameter is left blank, then the SELECT should default to NOT having the WHERE clause at all.
CREATE PROCEDURE sprocA
#flagInd int
AS
BEGIN
SELECT intID, strQuestion, strAnswer, intCategory, intOrder
FROM tblA
-- Right here is where I am looking for the logic of the #flagInd to be evaluated.....
-- if #flagInd = 1 then 'WHERE flgInd=1'
-- else if #flagInd=0 then 'WHERE flgInd=0'
-- else if #flagInd IS NULL then ''
It's just a simple query and a simple thought I had, not sure if it can be done without nesting and rewriting the whole SELECT statement as part of of the IF statement.
This can be done like:
SELECT intID, strQuestion, strAnswer, intCategory, intOrder
FROM tblA
WHERE flgInd = #flgInd OR #flgInd IS NULL;
You can also use a CASE expression:
SELECT intID, strQuestion, strAnswer, intCategory, intOrder
FROM tblA
WHERE CASE
WHEN flgInd = #flgInd THEN 1
WHEN #flgInd IS NULL THEN 1
ELSE 0
END = 1;
There appears to just be a one to one mapping (from the parameter to the column) so why not use a simple where clause?
CREATE PROCEDURE sprocA
#flagInd int
AS
BEGIN
SELECT intID, strQuestion, strAnswer, intCategory, intOrder
FROM tblA WHERE flgInd = #flagInd;

Sql how to switch tables

I have two tables one of them have historical(cdr_hist) data other table have data from today(cdr_stage). My script must run every 30 minutes and calculate data from last 4 hours but every night at 12 all data move at cdr_hist.
The question is how I can switch and take data from history table when script run at 12:00 because cdr_stage is empty...
I tried this:
IF OBJECT_ID ('[**CDR_Stage**]') IS NOT NULL
BEGIN
Select.....
From **CDR_Stage**
END
ELSE
Select.....
From **CDR_Hist**
END
But its not work correctly...
Any ideas??
No need for IFs , that can be done with pure sql using UNION and NOT EXISTS() :
SELECT * FROM CDR_Stage
UNION ALL
SELECT * FROM CDR_Hist
WHERE NOT EXISTS(SELECT 1 FROM CDR_Stage) -- Second select will return data only if first one won't .
You need to check the record existence instead of table existence
IF EXISTS (SELECT 1
FROM CDR_Stage)
SELECT *
FROM CDR_Stage
ELSE
SELECT *
FROM CDR_Hist
Or Dynamic Sql
DECLARE #sql VARCHAR(4000)
SET #sql = 'select * from '
+ CASE
WHEN EXISTS (SELECT 1
FROM CDR_Stage) THEN 'CDR_Stage'
ELSE 'CDR_Hist'
END
EXEC (#sql)

Exists vs select count

In SQL Server, performance wise, it is better to use IF EXISTS (select * ...) than IF (select count(1)...) > 0...
However, it looks like Oracle does not allow EXISTS inside the IF statement, what would be an alternative to do that because using IF select count(1) into... is very inefficient performance wise?
Example of code:
IF (select count(1) from _TABLE where FIELD IS NULL) > 0 THEN
UPDATE TABLE _TABLE
SET FIELD = VAR
WHERE FIELD IS NULL;
END IF;
the best way to write your code snippet is
UPDATE TABLE _TABLE
SET FIELD = VAR
WHERE FIELD IS NULL;
i.e. just do the update. it will either process rows or not. if you needed to check if it did process rows then add afterwards
if (sql%rowcount > 0)
then
...
generally in cases where you have logic like
declare
v_cnt number;
begin
select count(*)
into v_cnt
from TABLE
where ...;
if (v_cnt > 0) then..
its best to use ROWNUM = 1 because you DON'T CARE if there are 40 million rows..just have Oracle stop after finding 1 row.
declare
v_cnt number;
begin
select count(*)
into v_cnt
from TABLE
where rownum = 1
and ...;
if (v_cnt > 0) then..
or
select count(*)
into v_cnt
from dual
where exists (select null
from TABLE
where ...);
whichever syntax you prefer.
As Per:
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:3069487275935
You could try:
for x in ( select count(*) cnt
from dual
where exists ( select NULL from foo where bar ) )
loop
if ( x.cnt = 1 )
then
found do something
else
not found
end if;
end loop;
is one way (very fast, only runs the subquery as long as it "needs" to, where exists
stops the subquery after hitting the first row)
That loop always executes at least once and at most once since a count(*) on a table
without a group by clause ALWAYS returns at LEAST one row and at MOST one row (even of
the table itself is empty!)

Resources