This document aims to show the difference between Oracle SQL and the SQL generated by Microsoft Access when using the QBE interface. Although most of the time one need not be exposed to SQL in Access it can be useful to understand the structure of SQL and when it generates clauses and extra brackets unexpectedly. It is also useful to know how Access SQL differs from the more standard SQL of Oracle.
Knowing about SQL in Access is useful when:
Oracle uses SQL*Plus commands to control the way results are displayed, show the definition of a table, edit commands etc. Microsoft Access uses properties to control the format, give validation etc.
SQL commands are used to create, query and maintain a database. A command may be continued over several lines. The buffer can contain only a single SQL command. The command is terminated by a semicolon except in combo box SQL and graphic source SQL (and is executed if using ORACLE).
To see the SQL that is generated when using Access, press the SQL icon, or select SQL from the view menu. SQL can also be typed in if one selects SQL Specific from the Query menu but one must be aware that converting to a Select query later will mean that the SQL is lost. It can not be looked at with the grid. SQL specific is really only used when one wants to use SQL to create tables, send SQL to a non-Access database system or create more complex queries
Retrieving data from the database is the most common SQL
operation, and to perform this the select command is used.
The basic select command has two parts (clauses):
The SQL*Plus save command saves the commands in the buffer in a file of type SQL (see example). Microsoft Access holds data, queries, forms, reports and macros in one big .MDB file.
If a file ‘login.sql’ exists, the commands in it are executed automatically whenever SQL*Plus is entered. The Microsoft equivalent is to have a macro called autoexec containing commands which is run on opening the database
The database used for the rest of the examples in this document is a simplified version of one that was used to hold information for a study of the victims of assault treated by the Bristol Royal Infirmary.
There are three tables of data, named DISTRICT, VICTIM and FRACTURE.
For more detail about the database and why it was set up in this particular way, see Overview document.
Microsoft Access tables are normally built using Table button, but one can create a table using SQL by creating a new query, not adding any tables, and then select 'SQL specific' from the 'Query' menu' Then choose 'data definition'. Note that the datatypes are given differently(text instead of char, number(integer) instead of number(n,0) etc.
The SQL command create table creates a table. For example, to create the example ORACLE database tables:create table DISTRICT(
district char(15) primary key, /* district of Bristol */
population number(6), /* population of district */
m_unemp number(6), /* no of unemployed males */
f_unemp number(6)) /* no of unemployed females */ ;
create table VICTIM(
vno number(3) primary key, /* reference number of victim */
alcohol_24hr number(3), /* units of alcohol drunk over previous 24 hours */
alcohol_wk number(4), /* units of alcohol drunk in average week */
live_district char(15), /* district where victim lives */
assault_district char(15), /* district where assault occurred */
birth_date date, /* date of birth of victim */
assault_date date check (assault_date between '1-jul-85' and '31-dec-86'), /* date when assault occurred */
sex char(1), check (sex in ('m','f')), /* sex of victim */
weapon char(15), /* weapon used in assault */
income number(6,2)) /* weekly income */ ;
create table FRACTURE(
vno number(3) not null references victim(vno), /* reference number of victim */
fno number(2), /* fracture number (allows >1 fracture per victim) */
side char(1), /* side of body fracture was on */
bone char(15)) /* name of fractured bone */ ;
The maximum length of ORACLE table and column names is 30 characters (Access 64). You are recommended for efficiency to use fewer than 10 characters.
The maximum number of fields in an Oracle table is 240 (Access 255).
not null specifies that every entry in the table must have a value for that column.
In DISTRICT, each ‘district’ is defined to be unique and thus is designate to be the primary key (columns used to uniquely identify a table). In FRACTURE, the combination of ‘vno’ and ‘fno’ is defined to be unique so these two columns form the primary key.
VICTIM and FRACTURE are related by the columns vno. VICTIM and DISTRICT can be linked by district and assault_district or live_district. vno ’in FRACTURE, and assault_district and live_district are the foreign keys (one or more columns whose values are based on the primary key from another table and used to specify the relationship between two tables). The ‘references’ clause shows the relationship between tthe VICTIM and FRACTURE table.
In VICTIM, assault_date has a constraint which ensures that the field can contain only values between 1-jul-85 and 31-dec-86
Access:
Oracle:
Access
Oracle
Access
Note that referential constraints are normally added by editing the relationships diagram in Access, and validity is added by editing the properties of the table.
Any form built using SQL*FORMS or Access can incorporate these constraints automatically, provided they were defined before the form was built.
The foreign key ensures that records added to the fracture table will relate to a known victim. If you add a foreign key, it must be the same as the primary key of the related table and the primary key clause must have been used on that table definition.
Tables can have any number of indexes. There are two reasons for creating an index:
create unique index VICTIM_IND on VICTIM (vno);
| Purpose | SQL command |
| display tables created | select * from user_tables; |
| display column names in the tables | select * from user_tab_columns; |
| what indexes created | select * from user_indexes; |
| what views (access queries) created | select * from user_views; |
| synonyms created | select * from user_synonyms; |
| tables, views, synonyms and sequences | select * from user_catalog |
A synonym is an alternative name for a table (not possible in Access) - mostly used when accessing another person’s tables and you want to avoid prefixing the name of the table with the username.
File/Add-in Database Documentor is also very useful
Q1: Find the district names and population from the DISTRICT table. (The names of tables are given in capitals by convention; in fact, case is ignored except in text strings.)
| field | district | population |
| sort | ||
| show | X | X |
| criteria | ||
| or |
| field | district | population |
| sort | ||
| show | X | X |
| criteria | >15000 | |
| or |
Average drunk Maximum drunk
------------- -------------
8.07692 32
| field | avg drunk:avg(alcohol_24hr) | max drunk:max(alcohol_24hr) | live_district |
| sort | |||
| show | X | X | |
| criteria | Clifton | ||
| or |
| field | avg drunk:alcohol_24hr | max drunk:alcohol_24hr | live_district | live_district |
| total | avg | max | group by | where |
| sort | ||||
| show | X | X | X | |
| criteria | Clifton | |||
| or |
Q4: Find the average and maximum number of alcohol units consumed by victims living in a given district.
Average drunk Maximum drunk
------------- -------------
5.30769 16
| field | avg drunk:alcohol_24hr | max drunk:alcohol_24hr | live_district |
| sort | |||
| show | X | X | |
| criteria | [enter value for district] | ||
| or |
| field | avg drunk:alcohol_24hr | max drunk:alcohol_24hr | live_district |
| total | avg | max | where |
| sort | |||
| show | X | X | |
| criteria | [enter value for district] | ||
| or |
Q5: Find the average number of alcohol units consumed by victims living in a given district by giving the district name as a parameter to a saved query.
Q6: Find the average number of alcohol units consumed by victims living in Clifton and Redland.
| field | avg drunk:Clifton+Redland | live_district |
| total | avg | where |
| sort | ||
| show | X | |
| criteria | "Clifton" or "Redland" | |
| or |
| field | avg drunk:Clifton+Redland | live_district | live_district |
| total | avg | where | where |
| sort | |||
| show | X | ||
| criteria | "Clifton" | ||
| or | "Redland" |
Q7: Find the average number of alcohol units consumed by victims living in Clifton and Redland. This uses a different syntax to produce the same result as the previous query.
select avg(alcohol_24hr) "Avg drunk : . . .
*
ERROR at line 1: ORA-0972: identifier . . .
Q8: Find how many victims consumed between 5 and 10 alcohol units.
| field | number drinking 5-10 units:count(alcohol_24hr) | alcohol_24hr |
| sort | ||
| show | X | |
| criteria | between 5 and 10 | |
| or |
| field | number drinking 5-10 units:alcohol_24hr | alcohol_24hr |
| total | count | where |
| sort | ||
| show | X | |
| criteria | between 5 and 10 | |
| or |
| field | number spending more than 75% of income on alcohol:alcohol_24hr | income | alcohol_24hr*.5/income |
| sort | |||
| show | X | ||
| criteria | >0 | >.75 | |
| or |
| field | number spending more than 75% of income on alcohol:alcohol_24hr | income | alcohol_24hr*.5/income |
| total | count | where | where |
| sort | |||
| show | X | ||
| criteria | >0 | >.75 | |
| or |
Q10: Find the districts whose names begin with ‘St’ in which people were assaulted.
Single character "a?a" Single digit "a#a" Range of characters "[a-z]" Outside a range "[!a-z]" Not a digit "[!0-9]"
| field | assault_district |
| sort | |
| show | X |
| criteria | St* |
| or |
Q11: Find districts whose names begin with ‘st’ regardless of case.
| field | assault_district | ucase(assault_district) |
| sort | ||
| show | X | |
| criteria | ST* | |
| or |
Q12: Find how many assaults took place in each district.
| field | assault_district | assault_district> |
| total | group by | count |
| sort | asc | |
| show | X | X |
| criteria | ||
| or |
Q13: Find how many assaults took place in each district. Display them in descending order of number of assaults.
| field | assault_district | countofassault_district:assault_district | assault_district |
| total | group by | count | expression |
| sort | descending | ascending | |
| show | X | X | |
| criteria | |||
| or |
Q14: Find the districts in which more than 20 people had been assaulted.
| field | assault_district | over 20:assault_district |
| total | group by | count |
| sort | ascending | |
| show | X | X |
| criteria | >20 | |
| or |
Q15: Find how many of each sex had been assaulted.
| field | gender:iif(sex="m","male","female") | sex |
| total | group by | count |
| sort | ||
| show | X | X |
| criteria | ||
| or |
Q16: Find, for each district in which assaults took place, the average number of alcohol units consumed by each sex.
decode(sex, ’m’, alcohol_24hr, null)
selects the value of alcohol_24hr if sex = ‘m’; otherwise the value is set to the null value and is not included in the calculation of the average.
| field | assault_district | male:Avg(IIf([sex]='m',[alcohol_24hr],Null)) | female:Avg(IIf([sex]='f',[alcohol_24hr],Null)) |
| total | group by | expression | expression |
| sort | |||
| show | X | ||
| criteria | |||
| or |
| field | assault_district | expr1:IIf([sex]='m','male','female') | alcohol_24hr |
| total | group by | group by | avg |
| crosstab | row heading | column heading | value |
| sort | |||
| show | X | X | X |
| criteria | |||
| or |
Q17: Find the districts in which no one was assaulted.
| field | district | assault_district |
| sort | ||
| show | X | |
| criteria | is null | |
| or |
| field | district | exists(select * from victim where assault_distict=district) |
| sort | ||
| show | X | |
| criteria | false | |
| or |
Q18: Find the dates of assaults which occurred before 1986.
d Display the day as a number without a leading zero (1-31).
dd Display the day as a number with a leading zero (01-31).
ddd Display the day as an abbreviation (Sun-Sat).
dddd Display the day as a full name (Sunday-Saturday).
ddddd Display a date serial number as a complete date
(including day, month, and year) formatted according to the Short
Date Format setting in the International section of the Control Panel.
The default Short Date Format is m/d/yy.
dddddd Display a date serial number as a complete date (including day, month,
and year) formatted according to the Long Date Format setting in the
International section of the Control Panel.
The default Long Date Format is mmmm dd, yyyy.
w Display the day of the week as a number (1-7).
ww Display the week of the year as a number (1-54).
m Display the month as a number without a leading zero (1-12).
If m immediately follows h or hh, the minute rather than
the month is displayed.
mm Display the month as a number with a leading zero (01-12).
If m immediately follows h or hh, the minute rather than the
month is displayed.
mmm Display the month as an abbreviation (Jan-Dec).
mmmm Display the month as a full name (January-December)
| field | format:format(assault_date,"dddd dd mmmm yyyy") | wkdayno:datepart('w',assault_date) | dayno:datepart('d',assault_date) | monthno:datepart('m',assault_date) | year:datepart('yyyy',assault_date) | assault_date |
| sort | ||||||
| show | X | X | X | |||
| criteria | <#01/1/86# | |||||
| or |
| field | youngest:datediff('m',assault_date/12) | oldest:datediff('m',assault_date/12) |
| total | min | max |
| sort | ||
| show | X | X |
| criteria | ||
| or |
| field | assault_day:format(assault_date,"dddd") | count:* | datepart('w',assault_date) |
| total | group by | expression | group by |
| sort | ascending | ||
| show | X | X | |
| criteria | |||
| or |
Q21: Find which bones were fractured when the victim had consumed more than 20 alcohol units.
| field | weapon | bone | alcohol_24hr | vno |
| sort | ||||
| show | X | X | X | X |
| criteria | >20 | |||
| or |
Q22: Find how many victims had consumed the amount of alcohol units in specified ranges.
Alcoh Number ----- ------- None 149 1-5 90 6-10 121 11-15 67 16-20 36 >20 24 6 records selected.drop table DRINKRANGE;
| field | alc_range | number:alc_range | alc_min | alcohol_24hr |
| total | group by | count | group by | where |
| sort | ascending | |||
| show | X | X | ||
| criteria | between [alc_min] and [alc_max] | |||
| or |
Q23: Find the weapon used on the person who consumed the greatest number of alcohol units and how much was consumed.
| field | weapon | alcohol_24hr |
| sort | ||
| show | X | X |
| criteria | (select max(alcohol_24hr) from victim) | |
| or |
Q24: Find the weapons used on the victims who had consumed the greatest number of alcohol units in each assault district.
Q25: Find the number of assaults that have taken place in each district, and the percentage of the total number.
create view ALL_DIST(count_all) as
select count(assault_district)
from VICTIM;
column percent format 99.99
select assault_district "District", count_district "Count",
(count_district/count_all)*100 "Percent"
from ALL_DIST, EACH_DIST;
drop view EACH_DIST;
drop view ALL_DIST;
| field | count_all:count(vno) |
| sort | |
| show | X |
| criteria | |
| or |
| field | assault_district | district_count:* |
| total | group by | count |
| sort | ||
| show | X | X |
| criteria | ||
| or |
| field | assault_district | district_count | percent:district_count/count_all |
| sort | |||
| show | X | X | X |
| criteria | |||
| or |
| field | assault_district | district_count:* | percent:district_count/count_all | count_all |
| total | group by | expression | expression | group by |
| sort | ||||
| show | X | X | X | |
| criteria | ||||
| or |
Q26: Create a new table containing the district names and an estimate of the percentage unemployment (assuming that 60% of the population are of employable age). Add a new column ‘comments’ to the table. Update the dat a in the table, save the changes using commit, then check.
alter table UNEMP
add (comments char(22));
commit;
select district, pc_unemp
from UNEMP
where comments like ’High%’
order by pc_unemp desc;
| field | district | expr1:([m_unemp]+[f_unemp]) | population |
| table | district | district | |
| append to | district | pc_unemp | |
| criteria | >0 | ||
| or |
| field | comments | pc_unemp | |
| update to | "High unemployment area" | ||
| criteria | >20 | ||
| or |
| field | assault_district | vno | alcohol_24hr |
| sort | ascending | ascending | |
| show | X | X | X |
| criteria | B* | ||
| or |
Q28: Produce a file containing data values required by another package.
suppress echoing of commands
suppress display of results (for commands given from an SQL file)
switch off display of number of records selected
suppress pause after each page
suppress display of command after variable substitution
set line width to 132 (maximum width allowed is 500)
switch off page headings
start spooling output to a file, prompting for the filename
set echo off
set pagesize 0
set termout off
set feedback off
set pause off
set verify off
set linesize 132
set headings off
spool &filename
spool off
set pagesize 24
set termout on
set pause on
set feedback 1
set verify on
set linesize 80
set headings on
set echo on