Why is the else condition triggered when the when condition is fulfilled? - sql

select t.customer_type, t.balance_no
from test12 t
where t.customer_type=&a1 and t.currency=
(case when &a2 in t.currency then &a2
else 'ALL'
end);
I take a number for customer type and a string for currency from the user and return
the current row. If the currency entered by the user is not available in the table then I should return the row in which the currency value is 'all'. And if the currency entered by the user exists in the table, I need to return the row corresponding to it. But in my code, it doesn't work like that. If the currency entered by the user exists in the table then it returns rows that match both that currency and the else condition.
For example, if a2='USD' then it returns 2 rows, t.currency='USD' and t.currency='ALL',
if a2='ALL' or a value doesn't exist in the table then it works truly, returns 1 row, t.currency='ALL'. What is the problem? Why case-when doesn't work truly?

Your condition and its case expression are evaluated for every row in the table (that matched the customer type, if the optimiser filters that first). It isn't matching both the when and else for any single row.
For the row with 'AZN' neither the when or else result match, so that row is rejected.
For the row with 'USD' the when result is matched, so that row is included.
For the row with 'ALL' the else result is matched, so that row is included.
So if the currency exists you always match both that and 'ALL', and if it doesn't you only match 'ALL'.
The in t.currency suggests you might think that will search the whole table every time, but it is only looking at the data in one row at a time (so should be = not in).
Assuming you only expect a single match you could look for either value, order based on why it matched, and pick the first row:
select t.customer_type, t.balance_no
from test12 t
where t.customer_type = &a1
and t.currency in (&a2, 'ALL')
order by case when t.currency = &a2 then 1 else 2 end
fetch first 1 row only;
Otherwise you can explicitly apply the logic you seems to be expecting, checking the whole table with exists; something like:
select t.customer_type, t.balance_no
from test12 t
where t.customer_type = 1
and (
t.currency = &a2
or
(
t.currency = 'ALL'
and not exists (
select null
from test12
where customer_type = 1
and currency = &a2
)
)
)
or if you want to stick with in:
select t.customer_type, t.balance_no
from test12 t
where t.customer_type = &a1
and t.currency =
case
when &a2 in (select currency from test12 where customer_type = &a1)
then &a2
else 'ALL'
end;
Note that the in is now searching the whole table, via a subquery, not just looking at the current row.
db<>fiddle with fixed values instead of substitution variables.
Incidentally, you might find it easier to quote those, i.e. '&a2', then you don't need to include the quotes when prompted for the currency value.

I think you maybe add one condition (customer_type = &a1) to populate balance_no field's value from currency table.

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

How to get the result based on the condition in SQL?

Consider Issue Details table has Overall_Issues_ID and Fixed_Issues_ID Column. I need to get the result based on certain condition as below.
Condition: if the Overall_Issues_ID Value exists in any one of the Fixed_Issues_ID Column, then those ID should be consider as Fixed or else it is considered as Not Fixed.
I am using Oracle Version 10 G.
Join the same table twice with different alias names
select i1.overall_issues_id,
case when i2.overall_issues_id is not null
then 'fixed'
else 'not fixed'
end as is_fixed
from issues_details i1
left join issues_details i2 on i2.fixed_issues_id = i1.overall_issues_id
SQLFiddle demo
You can look up the ID in a subquery:
select
overall_issues_id as id,
case when overall_issues_id in (select fixed_issues_id from issue_details fixed)
then 'Fixed' else 'Not Fixed' end as fixed
from issue_details;

Access query inconsistently treats empty string as null

I have an application that is grabbing data from an Access database. I am seeking the minimum value of a column and the results I am getting back are inconsistent.
Have I run into a feature where Access inconsistently treating an empty string as a null depending on whether I add a filter or not, or is there something wrong with the way I am querying the data?
The column contains one blank value (not null) and several non-blank values that are all identical (about 30 instances of 'QLD'). The query I am using has a filter that involves multiple other tables, so that only the blank value and about half of the 'QLD' values are eligible.
It's probably easier to show the code and the effects rather than describe it. I have created a series of unioned queries which 'should' bring back identical results but do not.
Query:
SELECT 'min(LOC_STATE)' as Category
, min(LOC_STATE) as Result
FROM pay_run, pay_run_employee, employee, department, location
WHERE pr_id = pre_prid
AND em_location = loc_id
AND pre_empnum = em_empnum
AND em_department = dm_id
AND pr_date >= #2/24/2015#
AND pr_date <= #2/24/2016#
UNION ALL
(SELECT TOP 1 'top 1 LOC_STATE'
, LOC_STATE
FROM pay_run, pay_run_employee, employee, department, location
WHERE pr_id = pre_prid
AND em_location = loc_id
AND pre_empnum = em_empnum
AND em_department = dm_id
AND pr_date >= #2/24/2015#
AND pr_date <= #2/24/2016#
ORDER BY LOC_STATE)
UNION ALL
SELECT 'min unfiltered', min(loc_state)
FROM location
UNION ALL
(SELECT TOP 1 'iif is null', iif(loc_state is null, 'a', loc_state)
FROM location
ORDER BY loc_state)
Results:
Category Result
min(LOC_STATE) 'QLD'
top 1 LOC_STATE ''
min unfiltered ''
iif is null ''
If I do a minimum with the filter it brings back 'QLD' and not the empty string. At this stage it is possible that the empty string is not being included because it is treated as a null or the filter removes it.
The second query, which brings back the top 1 state using the filter shows that the empty string is not filtered out, which means that the Min function is ignoring the empty string.
The third query, which gets the minimum of the unfiltered table, brings back the empty string - so the minimum function does not exclude empty strings / treat them as null.
The fourth query, ensures that there is not a null in the empty string position.
My conclusion is that perhaps the inclusion of other tables and filter criteria is causing the empty string value to be treated as a null, but I feel that I must be missing something.
NB: I have a very similar query (date literals altered) that executes against the same data imported into a SQL Server database. It is correctly returning '' for all 4 queries.
Does anyone know why the empty string is ignored by the Min function in the first query?
PS: for those who prefer a query with joins
SELECT 'min(LOC_STATE)' as Category
, min(LOC_STATE) as Result
FROM (((pay_run
INNER JOIN pay_run_employee ON pay_run.pr_id = pay_run_employee.pre_prid)
INNER JOIN employee ON pay_run_employee.pre_empnum = employee.em_empnum)
INNER JOIN department ON employee.em_department = department.dm_id)
INNER JOIN location on employee.em_location = location.loc_id
WHERE
PR_DATE >= #2/24/2015# and
PR_DATE <= #2/24/2016#
union all
(SELECT TOP 1 'TOP 1 LOC_STATE'
, LOC_STATE
FROM (((pay_run
INNER JOIN pay_run_employee ON pay_run.pr_id = pay_run_employee.pre_prid)
INNER JOIN employee ON pay_run_employee.pre_empnum = employee.em_empnum)
INNER JOIN department ON employee.em_department = department.dm_id)
INNER JOIN location on employee.em_location = location.loc_id
WHERE
PR_DATE >= #2/24/2015# and
PR_DATE <= #2/24/2016#
order by LOC_STATE)
union all
select 'min unfiltered', min(loc_state)
from location
This has got nothing to do with corrupt data or unions or joins. The problem can be easily made visible by exectuting following queries in access:
create table testbug (Field1 varchar (255) NULL)
insert into testbug (Field1) values ('a')
insert into testbug (Field1) values ('')
insert into testbug (Field1) values ('c')
select min(field1) from testbug
To my opinion this is a bug in ms-access. When the MIN function in ms-access comes across an empty string ('') it forgets all the values he has come across and returns the minimum value from all the values below the empty string. (in my simple example only value 'c')

SQL - select data based on one specific input value or all if no input value

I am trying to retrieve data using an input variable that is a Customer code. If a user enters the customer code, the query retrieves that customers data, but if the user leaves the customer code blank, I want to retrieve all customers data. Below is the code where I can retrieve data based on the customer code input in '3_customer' but I can't figure out how to do this IF-THEN-ELSE type of query I need to get data for all customers if the input variable is left blank.
Thanks,
Don
SELECT
open_item.order_id order_id,
convert(varchar(10),orders.ordered_date,101) order_date,
orders.revenue_code_id,
orders.operations_user,
open_item.customer_id cust_no,
customer.name cust_name,
convert(varchar(10),open_item.gl_date,101) adjust_date,
open_item.amount amount,
open_item.record_type type,
open_item.ar_reason_code_id,
ar_reason_code.descr reason
FROM
open_item
LEFT OUTER JOIN
customer ON customer.id = open_item.customer_id
LEFT OUTER JOIN
ar_reason_code ON open_item.ar_reason_code_id = ar_reason_code.id
LEFT OUTER JOIN
orders ON orders.id = open_item.order_id
WHERE
open_item.gl_date >= $1_sdate$ AND
open_item.gl_date <= $2_edate$ AND
open_item.source = 'C' AND
open_item.record_type = 'C' AND
open_item.customer_id = $3_customer$
An alternative approach is to:
open_item.customer_id = coalesce($3_customer$, open_item.customer_ID)
assuming $3_customer$ will be NULL
so $3_customer$ must not be null so then use a case
open_item.customer_id = case when $3_customer$ = ''
then open_item.customer_ID else $3_customer$
coalesce takes the first non-null value in a series in essence this will always return TRUE when the customer parameter is null. It does this because it compares the same values thus always equaling; otherwise this will filter for the specific $3_customer$ provided
You need to slightly change the WHERE clause for the customer condition from this:
and open_item.customer_id = $3_customer$
to something like this: (The below code comes with the assumption that $3_customer$ is getting inserted into the query string, and will be set to NULL if empty)
and ($3_customer$ IS NULL OR open_item.customer_id = $3_customer$)

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