IIF statement with multiple conditions - sql

Consent:
IIf([consents].[decision]=1,"grant",
IIf([consents].[allowbreaktheglass]=0,"deny""Default"),
IIf([consents].[AllowBreakTheGlass]=1 AND [consents].[Decision]=0,"emergency only"))
IIF statement is returning an error:
The expression you entered has a function containing the wrong number of arguments
Criteria for consents are Grant, Deny and Emergency only.

Syntax for IIF statement is: IIf ( expr , truepart , falsepart )
You are missing falsepart in following 2:
Try Changing: IIf([consents].[allowbreaktheglass]=0,"deny""Default")
to: IIf([consents].[allowbreaktheglass]=0,"deny","Default")
and: IIf([consents].[AllowBreakTheGlass]=1 AND [consents].[Decision]=0,"emergency only")
to: IIf([consents].[AllowBreakTheGlass]=1 AND [consents].[Decision]=0,"emergency only","")
for more info visit: https://support.office.com/en-us/article/iif-function-32436ecf-c629-48a3-9900-647539c764e3

It's not 100% clear what you are trying to accomplish with your Nested IIF statement, however you are simply missing one failure argument, the following may be what you want:
Added 'Error?' for the last failure argument, as well as moved the 'Default' into the failure of the first clause.
Consent:
IIF([consents].[decision] = 1, 'grant',
IIF([consents].[allowbreaktheglass] = 0, 'deny',
IIF([consents].[AllowBreakTheGlass] = 1 AND [consents].[Decision] = 0,
'emergency only', 'Error?')) 'Default')

See whether you find Switch easier than nested IIf statements.
Open a new query in the Access query designer, switch to SQL View, and paste in this statement ...
SELECT
Switch(
c.[decision]=1, 'grant',
c.[allowbreaktheglass]=0, 'deny',
c.[AllowBreakTheGlass]=1 AND c.[Decision]=0, 'emergency only',
True, 'Default'
) AS Consent
FROM consents AS c;
Switch operates on expression/value pairs. It returns the value from the first pair whose expression evaluates as True, and ignores the remaining pairs regardless of whether or not they would be True.
Not sure I understood the logic for what you wanted, but that first pair would return 'grant' if [decision]=1. If not, it would examine the next pair. And so on. The last pair has True as its expression, so when none of the first 3 pairs are matched, the function will return 'Default'.

Related

Why does this access query work and the other does not?

I have a query that filters based on a boolean value on a form (Task Archived). The boolean value is stored as a long in the underlying table (only 0 and -1 are allowed).
I wanted to write a query that would either show only those records that are false (records not archived) or all records.
I would have expected the follwing query to work, but it does not... it returns only those records for which the archive field is set to True (-1):
SELECT tbl_Tasks.TaskID, tbl_Tasks.TArchive
FROM tbl_Tasks
WHERE (((tbl_Tasks.TArchive)=
IIf([Forms]![frm_Activity]![txtArchivedTaskDisplay]=0,False,
(tbl_Tasks.TArchive)=0 Or (tbl_Tasks.TArchive)=-1)));
The query below, which I would expect to show only those records that are True, in fact returns all records:
SELECT tbl_Tasks.TaskID, tbl_Tasks.TArchive
FROM tbl_Tasks
WHERE (((tbl_Tasks.TArchive)=
IIf([Forms]![frm_Activity]![txtArchivedTaskDisplay]=0,False,
(tbl_Tasks.TArchive)<>0)));
Why is this? What logic is access follwing here?
You cannot put SQL where condition within IIF. Anything within IIF will be evaluated if possible. Since you only have 0 or -1 below evaluation will always be true.
((tbl_Tasks.TArchive)=0 Or (tbl_Tasks.TArchive)=-1) => true
that's why you are only seeing TArchive = true
You could try something like this for your first query:
WHERE
tbl_Tasks.TArchive **<** IIf(displayArchivedOnly,0,1);
for the second one:
WHERE
tbl_Tasks.TArchive = [Forms]![frm_Activity]![txtArchivedTaskDisplay]
assuming your txtArchivedTaskDisplay holds either true or false value like a toggle button. but again, read more about how iif works.

Passing Optional List argument from Django to filter with in Raw SQL

When using primitive types such as Integer, I can without any problems do a query like this:
with connection.cursor() as cursor:
cursor.execute(sql='''SELECT count(*) FROM account
WHERE %(pk)s ISNULL OR id %(pk)s''', params={'pk': 1})
Which would either return row with id = 1 or it would return all rows if pk parameter was equal to None.
However, when trying to use similar approach to pass a list/tuple of IDs, I always produce a SQL syntax error when passing empty/None tuple, e.g. trying:
with connection.cursor() as cursor:
cursor.execute(sql='''SELECT count(*) FROM account
WHERE %(ids)s ISNULL OR id IN %(ids)s''', params={'ids': (1,2,3)})
works, but passing () produces SQL syntax error:
psycopg2.ProgrammingError: syntax error at or near ")"
LINE 1: SELECT count(*) FROM account WHERE () ISNULL OR id IN ()
Or if I pass None I get:
django.db.utils.ProgrammingError: syntax error at or near "NULL"
LINE 1: ...LECT count(*) FROM account WHERE NULL ISNULL OR id IN NULL
I tried putting the argument in SQL in () - (%(ids)s) - but that always breaks one or the other condition. I also tried playing around with pg_typeof or casting the argument, but with no results.
Notes:
the actual SQL is much more complex, this one here is a simplification for illustrative purposes
as a last resort - I could alter the SQL in Python based on the argument, but I really wanted to avoid that.)
At first I had an idea of using just 1 argument, but replacing it with a dummy value [-1] and then using it like
cursor.execute(sql='''SELECT ... WHERE -1 = any(%(ids)s) OR id = ANY(%(ids)s)''', params={'ids': ids if ids else [-1]})
but this did a Full table scan for non empty lists, which was unfortunate, so a no go.
Then I thought I could do a little preprocessing in python and send 2 arguments instead of just the single list- the actual list and an empty list boolean indicator. That is
cursor.execute(sql='''SELECT ... WHERE %(empty_ids)s = TRUE OR id = ANY(%(ids)s)''', params={'empty_ids': not ids, 'ids': ids})
Not the most elegant solution, but it performs quite well (Index scan for non empty list, Full table scan for empty list - but that returns the whole table anyway, so it's ok)
And finally I came up with the simplest solution and quite elegant:
cursor.execute(sql='''SELECT ... WHERE '{}' = %(ids)s OR id = ANY(%(ids)s)''', params={'ids': ids})
This one also performs Index scan for non empty lists, so it's quite fast.
From the psycopg2 docs:
Note You can use a Python list as the argument of the IN operator using the PostgreSQL ANY operator.
ids = [10, 20, 30]
cur.execute("SELECT * FROM data WHERE id = ANY(%s);", (ids,))
Furthermore ANY can also work with empty lists, whereas IN () is a SQL syntax error.

Conditional Query Expression

I have the below SQL in Access Query. But I need to add a condition to the Expression "Lasted".
SELECT ErrorLog.Reported_Date, ErrorLog.Details, ErrorLog.Started_At, ErrorLog.Ended_At, ErrorLog.Project_Name, ([ended_at]-[started_at]) AS Lasted, ErrorLog.Not_Count_For_Pro_Time
FROM ErrorLog
GROUP BY ErrorLog.Reported_Date, ErrorLog.Details, ErrorLog.Started_At, ErrorLog.Ended_At, ErrorLog.Project_Name, ([ended_at]-[started_at]), ErrorLog.Not_Count_For_Pro_Time;
This query is used as a Record Source for a report. As it stands now, the expression contains the value of " ([ended_at]-[started_at])". But I need to find a way to show 0 for "lasted", when "Not_Count_For_Pro_Time" is checked/True.
I have tried this code below, but it only shows the records where "Not_Count_for_Pro_Time" is false. I want the report to show all the records regardless if the "Not_Count_For_Pro_Time" is true or false, but the Expression "Lasted" should be adjusted as indicated earlier.
SELECT ErrorLog.Reported_Date, ErrorLog.Details, ErrorLog.Started_At, ErrorLog.Ended_At, ErrorLog.Project_Name, ([ended_at]-[started_at]) AS Lasted, ErrorLog.Not_Count_For_Pro_Time
FROM ErrorLog
GROUP BY ErrorLog.Reported_Date, ErrorLog.Details, ErrorLog.Started_At, ErrorLog.Ended_At, ErrorLog.Project_Name, ([ended_at]-[started_at]), ErrorLog.Not_Count_For_Pro_Time
HAVING (((ErrorLog.Not_Count_For_Pro_Time)=False));
Use conditional logic:
IIF(ErrorLog.Not_Count_For_Pro_Time = 'true', 0, [ended_at]-[started_at]) AS Lasted,

Update Table in Access

In MS-Access 2007, I have a table, [Test_Master] where I have a field [DT_REPORT]. I want to update [Test_Norm_Due] by 2 months if field [Size] = "small". If the field "Size" = "Med." then by 3 months. I create below query but it is throwing Syntax error. Can someone help.
UPDATE Test_Master
SET Test_Master.Test_Norm_Due =
IIF((([Test_Master]![Size]="small")), DateAdd(("m",2,[Test_Master]![DT_REPORT]))),
IIF((([Test_Master]![Size]="med.")), DateAdd(("m",3,[Test_Master]![DT_REPORT])));
I believe you have a problem with your parentheses - try nesting them using an external Text editor (like notepad++) for greater visibility - also, you are using extra parentheses that are getting in your way, try simplifying; and you're missing one final condition - what should happen with Test_Norm_Due when Size is neither "small" nor "med."
Note that syntax for IIF is:
IIF (condition, value if true, value if false).
You are nesting IIFs, so you should have something like:
IIF (condition, value if true, IIF(other condition, value if true, value if false))
Try something like this (I broke it in multiple lines just to try to make it more visible for you).
UPDATE Test_Master SET Test_Master.Test_Norm_Due =
IIF (([Test_Master]![Size]="small"),
DateAdd("m",2,[Test_Master]![DT_REPORT]),
IIF (([Test_Master]![Size]="med."),
DateAdd("m",3,[Test_Master]![DT_REPORT]),
{missing value - What happens if it's neither "small" nor "med."} ));

Access/jet equivalent of Oracle's decode

Is there an equivalent for Oracle's decode() in Access (or Jet, for that matter).
The problem I am facing is: I should sort (order) a resultset based basically upon
a status and a date (with all records having status = 2) at the end.
In Oracle I'd go something like
select
...
from
...
where
..
order by
decode(status, 2, 0, 1),
date_column
The closest analogy is the SWITCH() function e.g.
Oracle:
SELECT supplier_name,
decode(supplier_id, 10000, 'IBM',
10001, 'Microsoft',
10002, 'Hewlett Packard',
'Gateway') result
FROM suppliers;
Access Database Engine
SELECT supplier_name,
SWITCH(supplier_id = 10000, 'IBM',
supplier_id = 10001, 'Microsoft',
supplier_id = 10002, 'Hewlett Packard',
TRUE, 'Gateway') AS result
FROM suppliers;
Note that with the SWITCH() function you have to supply the full predicate each time, so you are not restricted to using just supplier_id. For the default value, use a predicate that is obvious to the human reader that it is TRUE e.g. 1 = 1 or indeed simply TRUE :)
Something that may not be obvious is that the logic in the SWITCH() function doesn't short circuit, meaning that every expression in the function must be able to be evaluated without error. If you require logic to short circuit then you will need to use nested IIF() functions.
You can try with IIF. See this stackoverflow question.
I think it might compare to switch or choose.
Switch(expr-1, value-1[, expr-2, value-2 … [, expr-n,value-n]])
-- http://office.microsoft.com/en-us/access/HA012289181033.aspx
Choose(index, choice-1[, choice-2, ... [, choice-n]])
-- http://msdn.microsoft.com/en-us/library/aa262690%28VS.60%29.aspx
You can use the SWITCH function:
LABEL: Switch(
[TABLE_NAME]![COL_NAME]='VAL1';'NEW_VAL1';
[TABLE_NAME]![COL_NAME]='VAL2';'NEW_VAL2';
)
Note semicolons and not commas.
The example above works in queries in MS Access 2010.