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.
Related
I've got a model method that conditionally concatenates the user's username ("login") and real name, if they've saved a real name - otherwise it just shows the username. I'd like to rewrite the query in ActiveRecord or Arel.
It looks like I should use an Arel::Nodes::NamedFunction. But i don't understand how to do the conditional concatenation with a named function. (Does Arel know about "if"? I can't find any reference in the docs.)
def primer_values
connection.select_values(%(
SELECT CONCAT(users.login,
IF(users.name = "", "", CONCAT(" <", users.name, ">")))
FROM users
ORDER BY IF(last_login > CURRENT_TIMESTAMP - INTERVAL 1 MONTH,
last_login, NULL) DESC,
contribution DESC
LIMIT 1000
)).uniq.sort
end
There's also similarly a conditional in ORDER BY.
While generally I abhor Raw SQL in rails given this usage I'd leave it as is. Although I might change it to something a bit more idiomatic like.
User
.order(
Arel.sql("IF(last_login > CURRENT_TIMESTAMP - INTERVAL 1 MONTH,last_login, NULL)").desc,
User.arel_table[:contribution].desc)
.limit(1000)
.pluck(Arel.sql(
'CONCAT(users.login,
IF(users.name = "", "",
CONCAT(" <", users.name, ">")))'))
.uniq.sort
Converting this to Arel without abstracting it into an object of its own will damage the readability significantly.
That being said just to give you an idea; the first part would be 3 NamedFunctions
CONCAT
IF
CONCAT
Arel::Nodes::NamedFuction.new(
"CONCAT",
[User.arel_table[:name],
Arel::Nodes::NamedFuction.new(
"IF",
[User.arel_table[:name].eq(''),
Arel.sql("''"),
Arel::Nodes::NamedFuction.new(
"CONCAT",
[Arel.sql("' <'"),
User.arel_table[:name],
Arel.sql("'>'")]
)]
)]
)
A NamedFunction is a constructor for FUNCTION_NAME(ARG1,ARG2,ARG3) so any SQL that uses this syntax can be created using NamedFunction including empty functions like NOW() or other syntaxes like LATERAL(query).
I currently have a table in a form that is populated by a query.I have drop-down in the form that I want to use to determine how a query gets sorted like this:
ORDER BY Switch(
[Forms]![Add/Find Programs]![sortField]="Program Title", programTitle,
[Forms]![Add/Find Programs]![sortField]="Department", department,
[Forms]![Add/Find Programs]![sortField]="Audience", audience,
[Forms]![Add/Find Programs]![sortField]="Method", method,
[Forms]![Add/Find Programs]![sortField]="Number", number)
This works how I want it to, except that when I select "Number" in my dropdown, it changes the data type of number to text somehow. The query sorts by 1,1,10,2,20,3... etc. I have tried using CInt() and Val(). It still sorts like text. Any ideas?
You can't have switch return a varying datatype. All branches must return the same datatype: here implicit conversion happens, and everything is converted to a string.
A simple workaround would be to separate the conditions into distinct ordering levels:
ORDER BY
IIF([Forms]![Add/Find Programs]![sortField]="Program Title", programTitle, null),
IIF([Forms]![Add/Find Programs]![sortField]="Department", department, null),
IIF([Forms]![Add/Find Programs]![sortField]="Audience", audience, null),
IIF([Forms]![Add/Find Programs]![sortField]="Method", method, null)
IIF([Forms]![Add/Find Programs]![sortField]="Number", number, null)
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'.
I am learning SQL so be gentle. If I have designated a specific role in my where clause it is only pulling those cases where that role is populated. How can I also include the NULL values or those roles that are blank?
Here is the where clause now:
WHERE (dbo.vcases.lawtype = 'My Cases') AND
(dbo.vcase_parties_people.role_sk = 4001) AND
**(v1.role_sk = 3940) AND
(v1.report_ind = 'Y') AND
(v2.role_sk = 3939) AND
(v2.report_ind = 'Y')** AND
(dbo.vcases.case_type NOT IN ('Case type 1', 'Case type 2'))
The COALESCE() expression in SQL is useful for substituting a default value when NULL is encountered for a given column or expression. When the query optimizer encounters a COALESCE() call, it will internally rewrite that expression to an equivalent CASE...WHEN expression. In your sample query, WHERE (COALESCE(v1.role_sk, 3940) = 3940) would operate (and optimize) the same as WHERE (CASE WHEN v1.role_sk IS NOT NULL THEN v1.role_sk ELSE 3940 END = 3940).
Since your example specifically involves a condition in the WHERE clause, you may want to use an OR operation, which could optimize better than a COALESCE() expression: WHERE (v1.role_sk = 3940 OR v1.role_sk IS NULL).
This is also assuming that any joins in your query aren't filtering out rows whose role_sk column is NULL.
You might edit your code as follows:
WHERE (dbo.vcases.lawtype = 'My Cases') AND
(dbo.vcase_parties_people.role_sk = 4001) AND
(v1.role_sk = 3940 OR v1.role_sk IS NULL) AND
(v1.report_ind = 'Y') AND
(v2.role_sk = 3939) AND
(v2.report_ind = 'Y') AND
(dbo.vcases.case_type NOT IN ('Case type 1', 'Case type 2'))
The use of the Coalesce function has been suggested but a good rule of thumb in SQL is to avoid the use of functions in the WHERE clause because it reduces the efficiency of the table's indexes. Functions in WHERE clause often cause Index-Scans instead of the more efficient Index-Seeks.
I have the following function:
CREATE OR REPLACE FUNCTION calc_a(BIDoctor number) RETURN number
IS
num_a number;
BEGIN
select count(NAppoint)
into num_a
from Appointment a
where BIDoctor = a.BIDoctor;
RETURN num_a;
END calc_a;
What we want is adding a column to a report that shows us the number of appointments that doc have.
select a.BIdoctor "NUM_ALUNO",
a.NameP "Nome",
a.Address "Local",
a.Salary "salary",
a.Phone "phone",
a.NumberService "Curso",
c.BIdoctor "bi",
calc_media(a.BIdoctor) "consultas"
FROM "#OWNER#"."v_Doctor" a, "#OWNER#"."Appointment" c
WHERE a.BIdoctor = c.BIdoctor;
and we got this when we are writing the region source on apex.
But it shows a parse error, I was looking for this about 2 hours and nothing.
Apex shows me this:
PARSE ERROR ON THE FOLLOWING QUERY
This is probably because of all your double quotes, you seem to have randomly cased everything. Double quotes indicate that you're using quoted identifiers, i.e. the object/column must be created with that exact name - "Hi" is not the same as "hi". Judging by your function get rid of all the double quotes - you don't seem to need them.
More generally don't use quoted identifiers. Ever. They cause far more trouble then they're worth. You'll know when you want to use them in the future, if it ever becomes necessary.
There are a few more problems with your SELECT statement.
You're using implicit joins. Explicit joins were added in SQL-92; it's time to start using them - for your future career where you might interact with other RDBMS if nothing else.
There's absolutely no need for your function; you can use the analytic function, COUNT() instead.
Your aliases are a bit wonky - why does a refer to doctors and c to appointments?
Putting all of this together you get:
select d.bidoctor as num_aluno
, d.namep as nome
, d.address as local
, d.salary as salary
, d.phone as phone
, d.numberservice as curso
, a.bidoctor as bi
, count(nappoint) over (partition by a.bidoctor) as consultas
from #owner#.v_doctor a
join #owner#.appointment c
on d.bidoctor = a.bidoctor;
I'm guessing at what the primary keys of APPOINTMENT and V_DOCTOR are but I'm hoping they're NAPPOINT and BIDOCTOR respectively.
Incidentally, your function will never have returned the correct result because you haven't limited the scope of the parameter in your query; you would have just counted the number of records in APPOINTMENT. When you're naming parameters the same as columns in a table you have to explicitly limit the scope to the parameter in any queries you write, for instance:
select count(nappoint) into num_a
from appointment a
where calc_a.bidoctor = a.bidoctor; -- HERE