Oracle SQL: Multiple lines of output per student - sql

I am working on a SQL statement for a vendor who's product's import feature calls for separate lines of data per student who are involved in special programs. For example:
Student ID Program Name
12345 Special Education
12345 Title 1
12345 Limited English
67891 Special Education
67891 Gifted and Talented
I'm not sure how to write the query statement to give me a separate line of data per student for each program they are involved with, instead of a single line with multiple columns. Can anyone get me pointed in the right direction?
My table structure is as follows
Table: Students
Relevant Columns:
student_number FLOAT(126)
last_name VARCHAR2(50 CHAR)
first_name VARCHAR2(50 CHAR)
iep NUMBER(10)
ellstatus NUMBER(10)
gifted NUMBER(10)
title1 NUMBER(10)
(plus hundreds of other non-relevant fields)
Thank you.

Without seeing the table structures, I am guessing your have the student info and the program info in separate tables. So you will do something like this:
SELECT s.StudentId, p.ProgramName
FROM students s
INNER JOIN programs p
ON s.studentid = p.studentid

I guess that you have the program information in each of the different columns (iep, ellstatus, gifted, title1). If this is the case; then it is not a normalized database, and it will probably give you some trouble later on.
As I don't know exactly how you map the number values of the iep, title1, gifted, ellstatus to the programs, I'll give you a way to select the numbers, giving one row for each student/field relationship. You can add the formatting to the query to display the program names as you expect.
This is using the union operator. This operator unites the result sets of two different queries. If you don't use union all, then the repeated rows will be displayed once only. I added all because I guess the numbers might be repeated.
select student_id, program from (
select student_id, iep program from students where iep is not null
union all
select student_id, title1 program from students where title1 is not null
union all
select student_id, gifted program from students where gifted is not null
union all
select student_id, ellstatus program from students where ellstatus is not null
);
To read more about the union operator, you can go here: http://docs.oracle.com/cd/B28359_01/server.111/b28286/queries004.htm

Related

PostgreSQL Insert into table with subquery selecting from multiple other tables

I am learning SQL (postgres) and am trying to insert a record into a table that references records from two other tables, as foreign keys.
Below is the syntax I am using for creating the tables and records:
-- Create a person table + insert single row
CREATE TABLE person (
pname VARCHAR(255) NOT NULL,
PRIMARY KEY (pname)
);
INSERT INTO person VALUES ('personOne');
-- Create a city table + insert single row
CREATE TABLE city (
cname VARCHAR(255) NOT NULL,
PRIMARY KEY (cname)
);
INSERT INTO city VALUES ('cityOne');
-- Create a employee table w/ForeignKey reference
CREATE TABLE employee (
ename VARCHAR(255) REFERENCES person(pname) NOT NULL,
ecity VARCHAR(255) REFERENCES city(cname) NOT NULL,
PRIMARY KEY(ename, ecity)
);
-- create employee entry referencing existing records
INSERT INTO employee VALUES(
SELECT pname FROM person
WHERE pname='personOne' AND <-- ISSUE
SELECT cname FROM city
WHERE cname='cityOne
);
Notice in the last block of code, where I'm doing an INSERT into the employee table, I don't know how to string together multiple SELECT sub-queries to get both the existing records from the person and city table such that I can create a new employee entry with attributes as such:
ename='personOne'
ecity='cityOne'
The textbook I have for class doesn't dive into sub-queries like this and I can't find any examples similar enough to mine such that I can understand how to adapt them for this use case.
Insight will be much appreciated.
There doesn’t appear to be any obvious relationship between city and person which will make your life hard
The general pattern for turning a select that has two base tables giving info, into an insert is:
INSERT INTO table(column,list,here)
SELECT column,list,here
FROM
a
JOIN b ON a.x = b.y
In your case there isn’t really anything to join on because your one-column tables have no column in common. Provide eg a cityname in Person (because it seems more likely that one city has many person) then you can do
INSERT INTO employee(personname,cityname)
SELECT p.pname, c.cname
FROM
person p
JOIN city c ON p.cityname = c.cname
But even then, the tables are related between themselves and don’t need the third table so it’s perhaps something of an academic exercise only, not something you’d do in the real world
If you just want to mix every person with every city you can do:
INSERT INTO employee(personname,cityname)
SELECT pname, cname
FROM
person p
CROSS JOIN city c
But be warned, two people and two cities will cause 4 rows to be inserted, and so on (20 people and 40 cities, 800 rows. Fairly useless imho)
However, I trust that the general pattern shown first will suffice for your learning; write a SELECT that shows the data you want to insert, then simply write INSERT INTO table(columns) above it. The number of columns inserted to must match the number of columns selected. Don’t forget that you can select fixed values if no column from the query has the info (INSERT INTO X(p,c,age) SELECT personname, cityname, 23 FROM ...)
The following will work for you:
INSERT INTO employee
SELECT pname, cname FROM person, city
WHERE pname='personOne' AND cname='cityOne';
This is a cross join producing a cartesian product of the two tables (since there is nothing to link the two). It reads slightly oddly, given that you could just as easily have inserted the values directly. But I assume this is because it is a learning exercise.
Please note that there is a typo in your create employee. You are missing a comma before the primary key.

SQL: combine two tables for a query

I want to query two tables at a time to find the key for an artist given their name. The issue is that my data is coming from disparate sources and there is no definitive standard for the presentation of their names (e.g. Forename Surname vs. Surname, Forename) and so to this end I have a table containing definitive names used throughout the rest of my system along with a separate table of aliases to match the varying styles up to each artist.
This is PostgreSQL but apart from the text type it's pretty standard. Substitute character varying if you prefer:
create table Artists (
id serial primary key,
name text,
-- other stuff not relevant
);
create table Aliases (
artist integer references Artists(id) not null,
name text not null
);
Now I'd like to be able to query both sets of names in a single query to obtain the appropriate id. Any way to do this? e.g.
select id from ??? where name = 'Bloggs, Joe';
I'm not interested in revising my schema's idea of what a "name" is to something more structured, e.g. separate forename and surname, since it's inappropriate for the application. Most of my sources don't structure the data, sometimes one or the other name isn't known, it may be a pseudonym, or sometimes the "artist" may be an entity such as a studio.
I think you want:
select a.id
from artists a
where a.name = 'Bloggs, Joe' or
exists (select 1
from aliases aa
where aa.artist = a.id and
aa.name = 'Bloggs, Joe'
);
Actually, if you just want the id (and not other columns), then you can use:
select a.id
from artists a
where a.name = 'Bloggs, Joe'
union all -- union if there could be duplicates
select aa.artist
from aliases aa
where aa.name = 'Bloggs, Joe';

PostgreSQL questions, constraints and queries

My task is to make a table that records the placement won by race car drivers competing in Race events.
The given schema is:
CREATE TABLE RaceEvent
(
Name text,
Year int,
);
CREATE TABLE Driver
(
Name text,
Date_of_birth date,
Gender char,
Nationality,
);
I then added the following constraints :
CREATE TABLE RaceEvent
(
RaceName text NOT NULL PRIMARY KEY,
Year int NOT NULL,
Description text NOT NULL
);
CREATE TABLE Driver
(
Name text NOT NULL,
Date_of_birth date NOT NULL PRIMARY KEY,
Gender char(1) NOT NULL,
Nationality text NOT NULL
);
The table I created looks like this :
CREATE TABLE Races
(
Medal char(6) CHECK (Medal = 'Gold' or Medal = 'Silver' or Medal =
'Bronze'),
Event text NOT NULL REFERENCES RaceEvent (Name),
DriverDOB date NOT NULL REFERENCES Driver (Date_of_birth)
);
I know using the date of birth as a primary key is very silly but for some reason that was part of the task.
I need to ensure a driver cannot gain multiple medals in the same race, can anybody give insight on a good way of doing this? I thought about using some sort of check but can't quite work it out.
After that, I need to write a query that can return the nationalities of drivers that won at least 2 gold medals in certain years, to figure out which nationalities seem to produce the best drivers. 2 versions of the same query, one using aggregation and one not.
I know I have to do something along these lines :
SELECT Nationality from Driver JOIN Races ON Driver.Date_of_Birth = Races.DriverDOB WHERE ....?
Not sure on what the best way of figuring out how to link the nationalities to the medals?
All feedback much appreciated
The "best" way to do it would be to restructure your schema, as right now it's pretty crap. I'm assuming you can't, so here's one way to prevent multiple drivers from gaining multiple medals in the same race: add a primary key on DriverDOB and Event to the Races table.
Try it out here: http://sqlfiddle.com/#!17/dc8a9/1
As for the query to get the nationalities with multiple golds in a given year, here's one way to do it:
SELECT d.nationality, COUNT(*) AS golds
FROM races r
JOIN driver d
ON r.driverdob = d.date_of_birth
JOIN raceevent e
ON r.event = e.racename
AND e.year = 1999
WHERE r.medal = 'Gold'
GROUP BY d.nationality
HAVING COUNT(*) > 1;
Output:
nationality golds
NatA 3
NatB 2
And you can test it here: http://sqlfiddle.com/#!17/dc8a9/9

Can I get some help me in writing a SQL query for searching multiple words in a table column

I am trying to write a query that searches a column of a table and returns the count with an alias name for multiple words.
select
count(STREET_ADDRESS) AS MELBOURNE
from
CUSTOMERS
where
STREET_ADDRESS like '%MELBOURNE%'
I am trying to do this for multiple cities in one query like Melbourne, Sydney, Auckland etc.
Try This,SELECT COUNT(*) FROM CUSTOMERS WHERE STREET_ADDRESS IN ('MANGERE','AVONDALE')GROUP BY STREET_ADDRESS
Keep in mind that the number of columns in the select is fixed. Maybe i missunderstood but you want to input a list of cities and have the output contain as many columns as input's you gave?
If not (fixed Nb cities):
Suggestion: Add conditions to the query (to be honest, since using wildcards no really perf enhancement, you should think about adding a "city" column otherwise if the table gets too big this query will become very slow)
SELECT
SUM(CASE WHEN STREET_ADDRESS LIKE '%MANGERE%' THEN 1 ELSE 0 END) AS MANGERE_COUNT,
SUM(CASE WHEN STREET_ADDRESS LIKE '%AVONDALE%' THEN 1 ELSE 0 END) AS AVONDALE_COUNT
FROM CUSTOMERS
WHERE
STREET_ADDRESS like '%MANGERE%'
OR
STREET_ADDRESS like '%AVONDALE%'
If that is the case:
You will need to create dynamic SQL to transpose the output.
Hope it helps
Sérgio
Have a derived table where you using a case expression returns the city name from each street_address. Then do GROUP BY on its result to count:
select count(*), city
from
(
select case when upper(STREET_ADDRESS) like '%MELBOURNE%' then 'Melbourne'
when upper(STREET_ADDRESS) like '%SYDNEY%' then 'Sydney'
when upper(STREET_ADDRESS) like '%AUCKLAND%' then 'Auckland'
end as city
from CUSTOMERS
) dt
group by city
Note: the UPPER() parts aren't needed if a case insensitive collation is used.

Mysql, reshape data from long / tall to wide

I have data in a mysql table in long / tall format (described below) and want to convert it to wide format. Can I do this using just sql?
Easiest to explain with an example. Suppose you have information on (country, key, value) for M countries, N keys (e.g. keys can be income, political leader, area, continent, etc.)
Long format has 3 columns: country, key, value
- M*N rows.
e.g.
'USA', 'President', 'Obama'
...
'USA', 'Currency', 'Dollar'
Wide format has N=16 columns: county, key1, ..., keyN
- M rows
example:
country, President, ... , Currency
'USA', 'Obama', ... , 'Dollar'
Is there a way in SQL to create a new table with the data in the wide format?
select distinct key from table;
// this will get me all the keys.
1) How do I then create the table using these key elements?
2) How do I then fill in the table values?
I'm pretty sure I can do this with any scripting language (I like python), but wanted to know if there is an easy way to do this in mysql. Many statistical packages like R and STATA have this command built in because it is often used.
======
To be more clear, here is the desired input output for a simple case:
Input:
country attrName attrValue key (these are column names)
US President Obama 2
US Currency Dollar 3
China President Hu 4
China Currency Yuan 5
Output
country President Currency newPkey
US Obama Dollar 1
China Hu Yuan 2
Cross-tabs or pivot tables is the answer. From there you can SELECT FROM ... INSERT INTO ... or create a VIEW from the single SELECT.
Something like:
SELECT country,
MAX( IF( key='President', value, NULL ) ) AS President,
MAX( IF( key='Currency', value, NULL ) ) AS Currency,
...
FROM table
GROUP BY country;
If you were using SQL Server, this would be easy using UNPIVOT. As far as I am aware, this is not implemented in MySQL, so if you want to do this (and I'd advise against it) you'll probably have to generate the SQL dynamically, and that's messy.
I think I found the solution, which uses VIEWS and INSERT INTO (as suggested by e4c5).
You have to get your list of AttrNames/Keys yourself, but MYSQL does the other heavy lifting.
For the simple test case above, create the new_table with the appropriate columns (don't forget to have an auto-increment primary key as well). Then
CREATE VIEW a
AS SELECT country, attrValue
WHERE attrName="President";
CREATE VIEW b
AS SELECT country, attrValue
WHERE attrName="Currency";
INSERT INTO newtable(country, President, Currency)
SELECT a.country, a.attrValue, b.attrValue
FROM a
INNER JOIN b ON a.country=b.country;
If you have more attrNames, then create one view for each one and then adjust the last statement accordingly.
INSERT INTO newtable(country, President, Currency, Capital, Population)
SELECT a.country, a.attrValue, b.attrValue, c.attrValue, d.attrValue
FROM a
INNER JOIN b ON a.country=b.country
INNER JOIN c ON a.country=c.country
INNER JOIN d ON a.country=d.country;
Some more tips
use NATURAL LEFT JOIN and you don't have to specify the ON clause