If Logic in a SQL Statement - sql

What I'm trying to do should be very simple but somehow I can't reach the right answer to my problem.
I've asked something similar in the past but the answer given to me previously no longer fits the requirements.
So here's what's going on - I need to conditionally select values from a table in my database in a different than the usual manner, like so:
Table:
Id int (not null)
ParentId int (not null)
EventOn DateTime (not null)
User int (null)
By the following select:
SELECT RST.* FROM RangeSheetTime RST
WHERE RST.[User] is not null
(in the above case, I take all the rows where the user isn't null)
Select RST.* FROM RangeSheetTime RST
WHERE RST.[User] is null
(in the above case, I take all the rows where the user is null)
So what am I trying to do? I want to build a select statement that when given a condition, such as EventOn < GETDATE(), will retrieve all the rows where the USER isn't null. In case there aren't any rows where USER isn't null, then it should retrieve the rows where it is null, if any.
How can I put this to work?
Note: I can't use if here, otherwise this would be easier.
EDIT:
I'm going to try to explain it the best I can. Imagine I have 3 rows for the same ParentId, 31.
2 of these rows have a column named StartOrEnd set to 1. There's just a difference between them,
for the 1st one, the USER column is null; for the 2nd one, the USER column has the value 90.
The 3rd row has the column StartOrEnd set to 0.
Now, I want to display results no matter the value of startorend. But there's a catch.
For every startorend, if there are more than 1 row and one of them has USER set to null and the others not null,
then only the non null rows for that startorend will display. but in case there are no non null rows for this condition, than the null values will display. I hope I was clear now.

You should look into the CASE...WHEN construct, which is the equivalent of IF...THEN in SQL:
https://web.archive.org/web/1/http://articles.techrepublic%2ecom%2ecom/5100-10878_11-5078041.html

This may work:
SELECT RST.* FROM RangeSheetTime RST
WHERE RST.[User] IS NOT NULL
UNION ALL
SELECT RST.* FROM RangeSheetTime RST
WHERE RST.[User] IS NULL
AND NOT EXISTS (
SELECT Id FROM RangeSheetTime
WHERE [User] IS NOT NULL
)
I have no means of testing this at the moment, so no guarantees.

If using sqlserver, look at the case statement, it might do what you want.
select a,b,
case when c is not null then 'The not null condition'
else 'The null condition
end [C]
from
Table T
where
condition
If using oracle, I think you can use decode.
Cheers.

Hallaghan, Are you asking about retrieval criteria or manipulating/ display of SQL result, if you are asking about manipulating or display of result case would help you else Union looks good to me.

id , parentid User , StartOrEnd
1 , 31, null , 1
2 , 31, 90 , 1
3 , 31, null , 0
and you want to say now, if startorEnd has multiple value for one parent id, you only want to see the row where user is not null , else the row with null user will come.
if it is like this, the answer given by Jeff should work

The final solution to this problem wasn't any easy to get at but after mashing our heads against a wall, me and a team mate got it down. Although every post from this topic helped us, none could really obtain the values we wanted, there were always flaws, but we are very thankful for all the help provided, without it we'd hardly have reached the answer by now.
So the solution is as follows:
select
P.ParentId RangeSheet
,coalesce(max(P.[End]), max(P.Start)) EventOn
,case when isnull(max(P.[End]), 0) = 0 then 1 else 0 end StartOrEnd
from
(
select
RST.ParentId
,case RST.StartOrEnd when 1 then RST.EventOn else null end Start
,case RST.StartOrEnd when 0 then RST.EventOn else null end [End]
from
(
select
coalesce(MAN.ParentId, AUT.ParentId) ParentId
,coalesce(MAN.StartOrEnd, AUT.StartOrEnd) StartOrEnd
,coalesce(max(MAN.CreatedOn), max(AUT.CreatedOn)) CreatedOn
from
(
-- Obter os manuais
select
RST.ParentId
,RST.StartOrEnd
,MAX(RST.CreatedOn) CreatedOn
from RangeSheetTime RST
where RST.[User] is not null
group by RST.ParentId, RST.StartOrEnd
) MAN
full outer join
(
-- Obter os automáticos
select
RST.ParentId
,RST.StartOrEnd
,MAX(RST.CreatedOn) CreatedOn
from RangeSheetTime RST
where RST.[User] is null
group by RST.ParentId, RST.StartOrEnd
) AUT on MAN.ParentId=AUT.ParentId and MAN.StartOrEnd=AUT.StartOrEnd
group by coalesce(MAN.ParentId, AUT.ParentId), coalesce(MAN.StartOrEnd, AUT.StartOrEnd)
) FOJ
inner join RangeSheetTime RST on FOJ.ParentId=RST.ParentId and FOJ.StartOrEnd=RST.StartOrEnd and FOJ.CreatedOn=RST.CreatedOn
) P
group by P.ParentId

Related

Query that detects difference between accounts/loads from TODAY and YESTERDAY

GOAL: DETECT any difference between yesterday's table loads and today's loads. Each load loads values of data that are associated with bank accounts. So I need a query that returns each individual account that has a difference, with the value in the column name.
I need data from several columns that are located from two different tables. AEI_GFXAccounts and AEI_GFXAccountSTP. Each time the table is loaded, it has a "run_ID" that is incremented by one. So it needs to be compared to MAX(run_id) and MAX(run_id) -1.
I have tried the following queries. All this query does is return all the columns I need. I now need to implement logic that runs these queries WHERE runID = MAX(runID). Then run it again where run_ID = Max(runID) -1. Compare the two tables, show the differences that can be displayed under columns like SELECT AccountBranch WHERE MAX(Run_ID) -1 AS WAS. etc. and another custom named column as 'IS NOW' etc for each column.
SELECT AEI_GFXAccounts.AccountNumber,
AccountBranch,
AccountName,
AccountType,
CostCenter,
TransactionLimit,
ClientName,
DailyCumulativeLimit
FROM AEI_GFXAccounts
JOIN AEI_GFXAccountSTP
ON (AEI_GFXAccounts.feed_id = AEI_GFXAccountSTP.feed_id
and AEI_GFXAccounts.run_id = AEI_GFXAccountSTP.run_id)
I use something similar to this to detect changes for a logging system:
WITH data AS (
SELECT
a.run_id,
a.AccountNumber,
?.AccountBranch,
?.AccountName,
?.AccountType,
?.CostCenter,
?.TransactionLimit,
?.ClientName,
?.DailyCumulativeLimit
FROM
AEI_GFXAccounts a
INNER JOIN AEI_GFXAccountSTP b
ON
a.feed_id = b.feed_id and
a.run_id = b.run_id
),
yest AS (
SELECT * FROM data WHERE run_id = (SELECT MAX(run_id)-1 FROM AEI_GFXAccounts)
),
toda AS (
SELECT * FROM data WHERE run_id = (SELECT MAX(run_id) FROM AEI_GFXAccounts)
)
SELECT
CASE WHEN COALESCE(yest.AccountBranch, 'x') <> COALESCE(toda.AccountBranch, 'x') THEN yest.AccountBranch END as yest_AccountBranch,
CASE WHEN COALESCE(yest.AccountBranch, 'x') <> COALESCE(toda.AccountBranch, 'x') THEN toda.AccountBranch END as toda_AccountBranch,
CASE WHEN COALESCE(yest.AccountName, 'x') <> COALESCE(toda.AccountName, 'x') THEN yest.AccountName END as yest_AccountName,
CASE WHEN COALESCE(yest.AccountName, 'x') <> COALESCE(toda.AccountName, 'x') THEN toda.AccountName END as toda_AccountName,
...
FROM
toda INNER JOIN yest ON toda.accountNumber = yestaccountNumber
Notes:
You didn't say which table some of your columns are from. I've prefixed them with ?. - replace these with a. or as. respectively (always good practice to fully qualify all your column aliases)
When you're repeating out the pattern in the bottom select (above ...) choose data for the COALESCE that will not appear in the column. I'm using COALESCE as a quick way to avoid having to write CASE WHEN a is null and b is not null or b is null and a is not null or a != b, but the comparison fails if accountname (for example) was 'x' yesterday and today it is null, because the null becomes 'x'. If you pick data that will never appear in the column then the check will work out because nulls will be coalesced to something that can never appear in the real data, and hence the <> comparison will work out
If you don't care when a column goes to null today from a value yesterday, or was null yesterday but is a value today, you can ditch the coalesce and literally just do toda.X <> yest.X
New accounts today won't show up until tomorrow. If you want them to show up do toda LEFT JOIN yest .... Of course all their properties will show as new ;)
This query returns all the accounts regardless of whether any changes have been made. If you only want a list of accounts with changes you'll need a where clause that is similar to your case whens:
WHERE
COALESCE(toda.AccountBranch, 'x') <> COALESCE(yest.AccountBranch, 'x') OR
COALESCE(toda.AccountName, 'x') <> COALESCE(yest.AccountName, 'x') OR
...
Do you have a date field? If so you can use Row_Number partitioned by your accounts. Exclude all accounts that have a max of 1 row 'New accounts", and then subtract the Max(rownumber) of each account's load by the Max(rownumber)-1's load. Only return accounts where this returned load is >0.You can also use the lag function to grab the previous accounts load instead of Max(rownumber)-1

ISNULL Function, replace null with 0

I am creating standings for the English Premier League. I need to make standings for home and away records. One of the teams has no home losses, therefore SQL counts this as a NULL not a 0. I want to replace the NULL with a 0. I am having trouble getting the desired results.
select ht.Team,
CASE when FTR IS NULL then 0
else count(ht.FTR)
END as LossesHome
Into dbo.HomeLoss
from dbo.HomeTeam ht
where FTR = 'A'
group by ht.Team, ht.FTR
I thought that this would give me the desired results, however, it only returns 19 teams (there are 20).
I have read other questions that use coalesce and isnull functions and tried them as well but I still only get 19 teams returned.
Any help would be appreciated. If anyone needs more information or code please let me know.
Thanks again.
Looks like the problem is in your WHERE clause where you are not including the FTR null value.
select ht.Team,
CASE when FTR IS NULL then 0
else count(ht.FTR)
END as LossesHome
Into dbo.HomeLoss
from dbo.HomeTeam ht
where FTR = 'A' or FTR is null
group by ht.Team
SELECT ISNULL(myColumn, 0 ) FROM myTable
you can use coalesce function:-
select ht.Team,
coalesce(count(ht.FTR) as count_LossesHome,0)
Into dbo.HomeLoss
from dbo.HomeTeam ht
where FTR = 'A'
group by ht.Team

Using SELECT with a display condition

SELECT DISTINCT Invoice.InvNo, Invoice.OrderNo, Part.PartNo,
orders.orddate AS Order_Date, Invoice.InvDate AS Bill_Date,
MiscChg.Descr, MiscChg.RegFee, Invoice.InvAmt,
Orders.ClaimNo, Firm.FirmName AS Ordering_Firm,
**oppatty.attyid(WHERE oppatty.attyfor = 13)**, Location.Name1 AS Location
The bolded section is the part I'm having trouble with. I know what I have isn't right, but it demonstrates what I would like to accomplish. In the oppatty table, there could be several items listed. I want it to only display "AttyID for the entry that has an ATTYFOR = 13".
Hope this make sense, thanks
Jack
You need to add a CASE WHEN to the select statement.
SELECT DISTINCT
Invoice.InvNo,
Invoice.OrderNo,
Part.PartNo,
orders.orddate AS Order_Date,
Invoice.InvDate AS Bill_Date,
MiscChg.Descr,
MiscChg.RegFee,
Invoice.InvAmt,
Orders.ClaimNo,
Firm.FirmName AS Ordering_Firm,
CASE WHEN oppatty.AttyFor = 13
THEN oppatty.AttyId
ELSE '' END AS attyfor,
Location.Name1 AS Location
FROM
.........
This will display the AttyId field when the row's AttyFor field is equal to 13 and show an empty string when it's not.
Your query has no from or where clause and your question is a bit jumbled, but even so, I think I understand what you want to do. Assuming it's acceptable to fill the "AttyID" values with null where "AttyFor" isn't equal to 13, then you could just use a case statement. Try something like this
select
stuff.things,
case
where oppatty.attyfor <> 13 then null
else oppatty.attyid
end as attyid,
stuff.others
from
oppatty
join stuff on oppatty.ID = stuff.ID
If that's not your desired result, and you'd rather entirely exclude rows where "AttyFor" isnt equal to 13, then just use a where clause.
select
stuff.things,
oppatty.attyid,
stuff.others
from
oppatty
join stuff on oppatty.ID = stuff.ID
where
oppatty.attyfor = 13

How to add Order by in sql query

update Room set Status = case
when Room_Rev.In_DateTime IS NOT NULL and Room_Rev.Out_DateTime IS NULL
then 'U'
when Room_Rev.In_DateTime IS NOT NULL and Room_Rev.Out_DateTime IS NOT NULL
then 'A'
when Room.Status!='R' and Room.Status!='U' and Room.Status!='A'
then Room.Status
else 'R'
end
FROM Room JOIN Room_Rev
ON Room.Room_ID=Room_Rev.Room_ID
and
((Room_Rev.Start_Date >= '2015-03-22' and Room_Rev.End_Date <= '2015-03-22')
OR
(Room_Rev.Start_Date<= '2015-03-22' and Room_Rev.End_Date> '2015-03-22')
OR
(Room_Rev.Start_Date< '2015-03-22' and Room_Rev.End_Date>= '2015-03-22'))
How to add order by Rev_ID desc in the query?
There are two table which is Room and Room_Rev,
they are one to many relationship
The last two row ROM0006 already fill the In_DateTime and Out_DateTime,
thus it regard check out,
and the last row insert new reservation,
the In_DateTime is null
thus i need the query return 'R' (Reserved status)
As one of the possible solutions I suggest a nested query instead of a join in UPDATE statement. The logic of the update is not completely clear to me, so I leave the final update for OP to correct order of sorting (Note I used top 1 and order by room_ID in the nested SELECT statement). However, this approach allows to handle all usual techniques applicable for a SELECT.
update Room set Status = (select TOP 1 case
when Room_Rev.In_DateTime IS NOT NULL and Room_Rev.Out_DateTime IS NULL
then 'U'
when Room_Rev.In_DateTime IS NOT NULL and Room_Rev.Out_DateTime IS NOT NULL
then 'A'
when Room.Status!='R' and Room.Status!='U' and Room.Status!='A'
then Room.Status
else 'R'
end
FROM Room_Rev
WHERE Room.Room_ID=Room_Rev.Room_ID
and
((Room_Rev.Start_Date >= '2015-03-22' and Room_Rev.End_Date <= '2015-03-22')
OR
(Room_Rev.Start_Date<= '2015-03-22' and Room_Rev.End_Date> '2015-03-22')
OR
(Room_Rev.Start_Date< '2015-03-22' and Room_Rev.End_Date>= '2015-03-22'))
ORDER BY Room_Rev.Room_Id
)
PS. As a piece of advise I still assume that such approach is not valid. It prevents proper normalization of data. You'd rather have this information always queried dynamically when required, instead of writing static value to ROOM.status

Why am I geting null when i use max instead of count

I answered the following question question link. But i fount stringe Behaviour.
when i write this
Update product Set [order]= Case when Not Exists (Select * from
product a where a.ProductTypeID =product.ProductTypeID and a.id
<product.ID )
tHEN 1
eLSE
((Select cOUNT([ORDER])+1 from product b where
b.ProductTypeID =product.ProductTypeID and product.ID <product.id)+1)
eND
It works well but when i write ...'
Update product Set [order]= Case when Not Exists (Select * from
product a where a.ProductTypeID =product.ProductTypeID and a.id
<product.ID )
tHEN 1
eLSE
((Select Max([ORDER])+1 from product b where
b.ProductTypeID =product.ProductTypeID and product.ID <product.id)+1)
eND
It's gives null in else situation i dont understand why?Can Anyone Explain this when i missing why its getting null when i use Max.Here is sql fiddle http://sqlfiddle.com/#!3/1e15d/1 where i use count when i use Max it gives null why?
The difference is that count returns zero for an empty result, but max returns null for an empty result.
You have product.ID <product.id in your condition in the subquery, which will always be false as you are comparing a field to itself. That will make the result from the subquery empty.
It should be b.ID <product.id to compare the value in the table in the subquery to a value in the table in the outer query.
So neither query works as intended, but when you use count you don't get a null value from the empty result.
you can try this(for mysql):
select ifnull(max(column), 0) when max() return null, it give you 0.