IF clause in SQL from statement - sql

I need a temporary solution to a problem I've created. Essentially I want to calculate two values, but use a different method dependant on the result of a condition.
select userReturnval, userregisterid, OtherValue
FROM
(
(SELECT otherValue
FROM...
) as tblO --unrelated table
,
(
if (select count(userregisterid) from table1 where site =#siteID and userid=#userID) >0
SELECT userReturnval, userregisterid
FROM
(
SELECT userReturnval, userregisterid, Rank() OVER (PARTITION BY .. ORDER BY ...) as RANK
FROM ...
WHERE --first where clause
) as tblRank
WHERE (RANK =1)
else
SELECT userReturnval, userregisterid
FROM
(
SELECT userReturnval, userregisterid, Rank() OVER (PARTITION BY .. ORDER BY ...) as RANK
FROM ...
WHERE --different where clause
) as tblRank
WHERE (RANK =1)
) as tblR
My if works fine on its own, I just to get it working as part of the larger query. At the moment, sqlserver doesn't like the if being in there.
Hopefully someone can point me in the right direction!

You can try using case statements in the WHERE clause, something like the statement below. Note that I don't think this will be particularly optimal for performance.
Doing it like this does allow you to keep it to a single statement though:
select userReturnval, userregisterid, OtherValue
FROM
(
(SELECT otherValue
FROM...
) as tblO --unrelated table
,
(
SELECT userReturnval, userregisterid
FROM
(
SELECT userReturnval, userregisterid, Rank() OVER (PARTITION BY .. ORDER BY ...) as RANK
FROM ...
WHERE
case --Choose which where clause to use
when (select count(userregisterid) from table1 where site =#siteID and userid=#userID) >0 then
case when /*First where clause*/ then 1
else 0
end
else
case when /*Second where clause*/ then 1
else 0
end
end
= 1
) as tblRank
WHERE (RANK =1)
) as tblR

Related

How do I exclude null duplicates without excluding valid nulls - SQL Server

I'm using a SQL Server and am trying to pull certain data. However, some rows pop up with duplicate order #s, where one line # is null while the other has a correct value.
The tricky thing is that there also are other line #s that are null, and if I were to do a filter that line_id is not null then I would exclude all the valid order #s with null values. Would I use a case statement? A subquery? I'm at a loss.
Here's an abridged version of my code and what I mean:
select
order_number
line_number
from table_1
With NOT EXISTS:
select t.*
from tablename t
where t.line_number is not null
or not exists (
select 1 from tablename
where order_number = t.order_number and line_number is not null
)
or with ROW_NUMBER() window function:
select t.order_number, t.line_number
from (
select *,
row_number() over (partition by order_number order by case when order_number is not null then 1 else 2 end) rn
from tablename
) t
where t.rn = 1

Records apart form max date

I have been helped by Metal to write a SQL as below
select id
, OrderDate
, RejectDate
, max(case when RejectDate = '1900-01-01' then '9999-12-31' else RejectDate end) as rSum
from tableA
group by id, OrderDate, RejectDate
Now, I would like to find out all the records for a partcular id below the max reject date to delete them from a transformation table
An option is to use row_number():
select
id,
OrderDate,
RejectDate
from (
select
t.*,
row_number() over(
partition by id
order by case when RejectDate = '1900-01-01' then '9999-12-31' else RejectDate end desc
) rn
from tableA t
) t
where rn > 1
The advantage of this technique is that it avoids aggregation, which may lead to better performance. Also, you can easily turn this into a delete statement by leveraging the concept of updateable CTE, as follows:
with cte as (
select
row_number() over(
partition by id
order by case when RejectDate = '1900-01-01' then '9999-12-31' else RejectDate end desc
) rn
from tableA t
)
delete from cte where rn > 1
This should work...
SELECT *
FROM tableA t1
INNER JOIN (
SELECT ID, MAX(RejectDate) as MaxRejectDate
FROM tableA) t2 ON t1.ID = t2.ID
WHERE t1.RejectDate < t2.MaxRejectDate

Will the query inside the ORDER BY be repeatedly evaluated?

I have several database objects which need to extract a single record (like TOP 1) from a table, but the priority for which one is chosen depends on a BIT value in a settings table, and that settings table will contain only one row.
I have written a view which will perform the required functionality:
CREATE VIEW TopOrganisationAddresses AS
WITH cte AS
(
SELECT OrganisationID,
AddressID,
CASE WHEN EXISTS (SELECT * FROM GlobalSettings WHERE DeliveryAddressInReports=1) THEN IsDeliveryAddress ELSE IsInvoiceAddress END AS OrderFirst,
CASE WHEN EXISTS (SELECT * FROM GlobalSettings WHERE DeliveryAddressInReports=1) THEN IsInvoiceAddress ELSE IsDeliveryAddress END AS OrderSecond
FROM OrganisationAddresses
)
SELECT OrganisationID, AddressID,
ROW_NUMBER() OVER(PARTITION BY OrganisationID
ORDER BY OrderFirst DESC, OrderSecond DESC) AS [Row]
FROM cte
Will the SELECT * FROM GlobalSettings queries be evaluated for every fow in the OrganisationAddresses table? If so this would be incredibly wasteful as it is only a static value that isn't going to change.
In theory it could be optimised away but you can perhaps just use another cte which does that SELECT to guarantee it e.g.
CREATE VIEW TopOrganisationAddresses AS
WITH cteg AS
(
SELECT CASE WHEN EXISTS (SELECT * FROM GlobalSettings WHERE DeliveryAddressInReports=1) THEN 1 ELSE 0 END AS dair
),
cte AS
(
SELECT OrganisationID,
AddressID,
CASE WHEN cteg.dair = 1 THEN IsDeliveryAddress ELSE IsInvoiceAddress END AS OrderFirst,
CASE WHEN cteg.dair = 1 THEN IsInvoiceAddress ELSE IsDeliveryAddress END AS OrderSecond
FROM OrganisationAddresses
CROSS JOIN cteg
)
SELECT OrganisationID, AddressID,
ROW_NUMBER() OVER(PARTITION BY OrganisationID
ORDER BY OrderFirst DESC, OrderSecond DESC) AS [Row]
FROM cte
SQL Server should be smart enough to run the queries only once. The optimizer should recognize exactly what you do -- that these are constant values. For instance, the following query returns the same value for the id:
with cte as (
select v.*,
(select top 1 newid() from (values (1), (2)) v2(n) order by newid()) as val
from (values (1), (2)) v(n)
)
select *
from cte;
(Here is a db<>fiddle.)
If you wanted to be sure that these are evaluated only once, you can move the logic to the FROM clause:
CREATE VIEW TopOrganisationAddresses AS
SELECT oa.OrganisationID, oa.AddressID,
ROW_NUMBER() OVER (PARTITION BY oa.OrganisationID
ORDER BY v.OrderFirst DESC, v.OrderSecond DESC
) AS [Row]
FROM OrganisationAddresses oa CROSS JOIN
(VALUES (CASE WHEN EXISTS (SELECT * FROM GlobalSettings WHERE DeliveryAddressInReports = 1) THEN IsDeliveryAddress ELSE IsInvoiceAddress END,,
CASE WHEN EXISTS (SELECT * FROM GlobalSettings WHERE DeliveryAddressInReports = 1) THEN IsInvoiceAddress ELSE IsDeliveryAddress END)
) v(OrderFirst, OrderSecond);

get only row that meet condition if such row exist and if not get the row that meet another condition

this sounds like a simple question but I just cant find the right way.
given the simplified table
with t as (
select ordernumber, orderdate, case when ordertype in (5,21) then 1 else 0 end is_restore , ordertype, row_number() over(order by orderdate) rn from
(
select to_date('29.08.08','DD.MM.YY') orderdate,'313' ordernumber, 1 as ordertype from dual union all
select to_date('13.03.15','DD.MM.YY') orderdate, '90/4/2' ordernumber, 5 as ordertype from dual
)
)
select * from t -- where clause should be here
for every row is_restore guaranteed to be 1 or 0.
if table has a row where is_restore=1 then select ordernumber,orderdate of that row and nothing else.
If a table does not have a row where is_restore=1 then select ordernumber,orderdate of the row where rn=1(row where rn=1 is guaranteed to exist in a table)
Given the requirements above what do I need to put in where clause to get the following?
You could use ROW_NUMBER:
CREATE TABLE t
AS
select ordernumber, orderdate,
case when ordertype in (5,21) then 1 else 0 end is_restore, ordertype,
row_number() over(order by orderdate) rn
from (
select to_date('29.08.08','DD.MM.YY') orderdate,'313' ordernumber,
1 as ordertype
from dual union all
select to_date('13.03.15','DD.MM.YY') orderdate, '90/4/2' ordernumber,
5 as ordertype
from dual);
-------------------
with cte as (
select t.*,
ROW_NUMBER() OVER(/*PARTITION BY ...*/ ORDER BY is_restore DESC, rn) AS rnk
from t
)
SELECT *
FROM cte
WHERE rnk = 1;
db<>fiddle demo
Here is sql, that doesn't use window functions, maybe it will be useful for those, whose databases don't support OVER ( ... ) or when there are indexed fields, on which query is based.
SELECT
*
FROM t
WHERE t.is_restore = 1
OR (
NOT EXISTS (SELECT 1 FROM t WHERE t.is_restore = 1)
AND t.rn = 1
)

Select and sums from another table. Whats wrong with this SQL?

Whats wrong with this SQL?
SELECT Id, (select SUM(VALUE) from SomeTable) AS SumValue, GETDATE()
FROM MyTable
WHERE SumValue > 0
You cannot use aliased columns in the SELECT clause in the same query, except in ORDER BY.
It needs to be subqueried
SELECT Id, SumValue, GETDATE()
FROM (
SELECT Id, (select SUM(VALUE) from TABLE) AS SumValue
FROM MyTable
) X
WHERE SumValue > 0
That is the general case. For your specific query, it doesn't make sense because the subquery is not correlated to the outer query, so either NO rows show, or ALL rows show (with the same SumValue). I will simply assume you have simplified the query a lot since a table name of "table" doesn't really work.
I would probably rewrite like this:
SELECT a.Id, b.SumValue, GETDATE() as [now]
FROM MyTable a
Join
(
select id, SUM(VALUE) as [SumValue]
from [TABLE]
Group by id
)b on a.Id = b.Id
WHERE b.SumValue > 0
This is assuming that the value you are totalling relates to the ID in your table?
right way is
SELECT Id, (select SUM(VALUE) from TABLE) AS SumValue, GETDATE()
FROM MyTable
WHERE (select SUM(VALUE) from TABLE) > 0