I an trying to INSERT multiple rows into an SQL, Oracle table though SQL Developer v3.0.04
the Database was set-up by Uni so I don't know what version it is.
after looking on-line I have come up with the code below but it will not INSERT any data. I have tested the insert with just one row and that is OK. What am I missing?
Insert all
Into Patient Values
('101', '1 house', Null, 'Kingston', 'Surrey', 'KT1 1XX', '10/jan/1980', 'm', 01452987456)
Into Patient Values
('102', '2 egg rd', 'vail', 'guildford', 'Surrey', 'GU1 1LL', '05/dec/1985', 'm', 01452987456)
Into Patient Values
('103', '6 station rd', Null, 'guildford', 'Surrey', 'GU1 2XX', '15/may/1990', 'f', 01452987456)
Select * from Patient;
INSERT ALL has two different uses. One is to insert different sub-sets of selected columns into a table. The other is to direct rows into different according to certain criteria. In both cases the data comes from a SELECT clause rather than from VALUES. See the examples in the documentation.
Normally, you would simply write multiple INSERT statements potentially in a single PL/SQL block. Something like
begin
Insert Into Patient Values
('101', '1 house', Null, 'Kingston', 'Surrey', 'KT1 1XX', '10/jan/1980', 'm', 01452987456);
Insert Into Patient Values
('102', '2 egg rd', 'vail', 'guildford', 'Surrey', 'GU1 1LL', '05/dec/1985', 'm', 01452987456);
Insert Into Patient Values
('103', '6 station rd', Null, 'guildford', 'Surrey', 'GU1 2XX', '15/may/1990', 'f', 01452987456);
end;
/
If you really want to do this in a single SQL statement, you can do an INSERT ... SELECT but that's generally going to be more complex than using three separate statements.
insert into patient
select *
from (select '101' id, '1 house' addr, null col1, 'Kingston' city, ...
from dual
union all
select '102', '2 egg rd', 'vail', 'guildford', 'Surrey', 'GU1 1LL', '05/dec/1985', 'm', 01452987456
from dual
union all
select '103', '6 station rd', Null, 'guildford', 'Surrey', 'GU1 2XX', '15/may/1990', 'f', 01452987456
from dual)
I would also caution you to use proper data types and to specify the column names in your INSERT statement. I'm guessing, for example, that the first column of Patient table is some sort of PatientID that is defined as a NUMBER. If so, you'd really want to insert a number rather than a character string. Similarly, the seventh column with values like '15/may/1990' is probably defined as a DATE in the table. If so, your INSERT should insert a DATE not a character string by either explicitly calling TO_DATE with a particular format mask or by using the ANSI date format, i.e. date '1980-01-10'. And if you want the last column to retain the leading 0, you'll need to ensure that the column in the database is defined as a VARCHAR2 and that you insert a character string rather than a number.
Interesting. It is possible to use insert all to insert multiple rows as you are attempting. Not that I would recommend doing so over Justin's suggested solutions.
The syntax diagram for multi-table insert indicates that a subquery is always required. However, you don't have to use any of the results of the subquery:
SQL> drop table t;
Table dropped.
SQL> create table t (c1 number, c2 varchar2(10));
Table created.
SQL> insert all into t (c1, c2) values (1, 'one')
2 into t (c1, c2) values (2, 'two')
3 select * from dual;
2 rows created.
SQL> select * from t;
C1 C2
---------- ----------
1 one
2 two
The two into ... values(...) will cause two rows to be inserted per row in the the subquery:
SQL> insert all into t (c1, c2) values (1, 'one')
2 into t (c1, c2) values (2, 'two')
3 select * from dual
4 connect by level <= 10;
20 rows created.
Replace this query [ Select * from Patient; ] with
Select * from dual;
dual is a virtual table which is not existing in our schema but it can be existed as a part of Oracle perspective in RAM not in our storage
Insert all
Into Patient Values
('101', '1 house', Null, 'Kingston', 'Surrey', 'KT1 1XX', '10/jan/1980', 'm', 01452987456)
Into Patient Values
('102', '2 egg rd', 'vail', 'guildford', 'Surrey', 'GU1 1LL', '05/dec/1985', 'm', 01452987456)
Into Patient Values
('103', '6 station rd', Null, 'guildford', 'Surrey', 'GU1 2XX', '15/may/1990', 'f', 01452987456)
Select * from dual;
Related
Here I am creating three tables, one for storing the Train Info. Another for holding onto the Passenger info and the other one to hold the ticket Info.
create table T_Train_Info(
route_no integer primary key,
source varchar2(50),
destination varchar2(50)
)
create table T_Pay_Info(
pax_id integer primary key,
pax_name varchar2(50),
dob date,
gender varchar2(5)
)
create table T_Tkt_Info(
pax_id integer,
route_no integer,
journey_date date,
seat_no varchar2(5),
primary key(pax_id, route_no, journey_date)
)
In the Train_Info table, I am inserting two unique routes with the same source and destination as there can be different routes for the same source and destination. And filling the other tables in the same manner. In the ticket table, I am repeating values because I aim to find the passenger travelling thrice on the same route.
insert into T_Train_Info values(1, 'Chennai', 'Pune');
insert into T_Train_Info values(2, 'Chennai', 'Pune');
insert into T_Train_Info values(3, 'Bangalore', 'Kolkata');
insert into T_Tkt_Info values(100, 1, to_date('11/03/2022', 'DD/MM/YYYY'), 22);
insert into T_Tkt_Info values(100, 1, to_date('14/08/2022', 'DD/MM/YYYY'), 23);
insert into T_Tkt_Info values(100, 1, to_date('29/08/2022', 'DD/MM/YYYY'), 24);
insert into T_Tkt_Info values(102, 3, to_date('22/08/2022', 'DD/MM/YYYY'), 24);
insert into T_Tkt_Info values(100, 1, to_date('27/08/2022', 'DD/MM/YYYY'), 24);
insert into T_Tkt_Info values(100, 2, to_date('28/08/2022', 'DD/MM/YYYY'), 24);
insert into T_Pay_Info values(100, 'A', to_date('11/03/2022', 'DD/MM/YYYY'), 'F');
insert into T_Pay_Info values(101, 'B', to_date('23/09/2023', 'DD/MM/YYYY'), 'M');
insert into T_Pay_Info values(102, 'A', to_date('11/03/2022', 'DD/MM/YYYY'), 'F');
insert into T_Pay_Info values(103, 'D', to_date('23/09/2023', 'DD/MM/YYYY'), 'M');
insert into T_Pay_Info values(104, 'A', to_date('11/03/2022', 'DD/MM/YYYY'), 'F');
insert into T_Pay_Info values(105, 'A', to_date('23/09/2023', 'DD/MM/YYYY'), 'M');
Here's my procedure which keeps returning the error saying 'exact fetch returns more than requested number of rows' at the select statement. What am I doing wrong here?
create or replace procedure pr_pass_route_details(x in T_Train_Info.Source%type, y in T_Train_Info.Destination%type) is
pr_name T_Pay_Info.Pax_Name%type;
begin
for i in (select pax_id from t_tkt_info group by pax_id,route_no having count(*) >=3) loop
select pax_name into pr_name from t_pay_info where pax_id = i.pax_id and T_Train_Info.Source=x and T_Train_Info.Destination=y;
dbms_output.put_line(pr_name);
end loop;
end pr_pass_route_details;
i’m not sure why you’ve written a SP to do this as you can achieve this with a simple query:
SELECT
pax_id,
route_no,
COUNT(journey_date)
FROM T_Tkt_Info
GROUP BY
pax_id,
route_no
HAVING COUNT(journey_date) = 3
I am trying to figure out a query for this question: for each major, list the number of students, minimum GPA, maximum GPA, average GPA, minimum age, maximum age, and average age. (Show GPA with 2 decimal points, age with no decimal points. You may find it useful to create a view with one of the previous queries for this one.)
This is the script to create the table for SQL!
REM drop all the tables. Note that you need to drop the
REM dependent table first before dropping the base tables.
drop table Reg;
drop table Student;
drop table Course;
REM Now create all the tables.
create table Student
(
sid char(10) primary key,
sname varchar(20) not null,
gpa float,
major char(10),
dob DATE
);
create table Course
(
cno char(10) primary key,
cname varchar(20) not null,
credits int,
dept char(10)
);
create table Reg
(
sid references Student(sid) on delete cascade,
cno references Course(cno) on delete cascade,
grade char(2),
primary key (sid, cno)
);
REM Now insert all the rows.
insert into Student values ('111', 'Joe', 3.5 , 'MIS', '01-AUG-2000');
insert into Student values ('222', 'Jack', 3.4 , 'MIS', '12-JAN-1999');
insert into Student values ('333', 'Jill', 3.2 , 'CS', '15-MAY-1998');
insert into Student values ('444', 'Mary', 3.7 , 'CS', '17-DEC-2001');
insert into Student values ('555', 'Peter', 3.8 , 'CS', '19-MAR-1999');
insert into Student values ('666', 'Pat', 3.9, 'Math', '31-MAY-2000');
insert into Student values ('777', 'Tracy', 4.0, 'Math', '18-JUL-1997');
insert into Course values ('c101', 'intro', 3 , 'CS');
insert into Course values ('m415', 'database', 4 , 'Bus');
insert into Course values ('m215', 'programming', 4 , 'Bus');
insert into Course values ('a444', 'calculus', 3 , 'Math');
insert into Reg values ('111', 'c101', 'A');
insert into Reg values ('111', 'm215', 'B');
insert into Reg values ('111', 'm415', 'A');
insert into Reg values ('222', 'm215', 'A');
insert into Reg values ('222', 'm415', 'B');
insert into Reg values ('333', 'c101', 'A');
insert into Reg values ('444', 'm215', 'C');
insert into Reg values ('444', 'm415', 'B');
insert into Reg values ('555', 'c101', 'B');
insert into Reg values ('555', 'm215', 'A');
insert into Reg values ('555', 'm415', 'A');
insert into Reg values ('666', 'c101', 'A');
This is what I have so far:
SELECT major,
count(distinct SID) as students,
round(min(gpa), 2),
round(max(gpa), 2),
round(avg(gpa), 2),
trunc(min(sysdate - dob)/365) as min_age,
trunc(max(sysdate - dob)/365) as max_age,
trunc(avg(sysdate - dob)/365) as avg_age,
FROM Student
GROUP BY MAJOR;
According to your input I've made a query that I belive will show you the results. (It was kind hard to read the tables the way you posted it). The syntax may differ according to your DBMS (SQL Server, MySQL, REdshift, Postgres, etc)
Here is the query:
SELECT major,
COUNT(*) as students,
ROUND(MIN(gpa), 2) as min_gpa,
ROUND(MAX(gpa), 2) as max_gpa,
ROUND(AVG(gpa), 2) as avg_gpa,
MIN(DATEDIFF(year, current_date, dob)) as min_age,
MAX(DATEDIFF(year, current_date, dob)) as max_age,
AVG(DATEDIFF(year, current_date, dob)) as avg_date
FROM students st left join Course co on co.dept = st.major
GROUP BY major
Your query is completely fine (just remove comma(,) after avg_age.
SELECT major,
count(distinct SID) as students,
round(min(gpa), 2) as MinGPA,
round(max(gpa), 2) as MaxGPA,
round(avg(gpa), 2) as AvgGPA,
round(min(sysdate - dob)/365,0) as min_age,
round(max(sysdate - dob)/365,0) as max_age,
round(avg(sysdate - dob)/365,0) as avg_age
FROM Student
GROUP BY MAJOR;
You can also use months_between() with floor() to get the same result:
select * from student;
SELECT major,
count(distinct SID) as students,
round(min(gpa), 2) as MinGPA,
round(max(gpa), 2) as MaxGPA,
round(avg(gpa), 2) as AvgGPA,
floor(min(months_between(trunc((sysdate)), dob)) /12) as min_age,
floor(max(months_between(trunc((sysdate)), dob)) /12) as max_age,
floor(avg(months_between(trunc((sysdate)), dob)) /12) as avg_age
FROM Student
GROUP BY MAJOR;
select *
from Address
where city in ('a', 'b', 'c', 'd', 'e', 'f', ... )
How can I modify the SQL to get only the cities provided in the query that are missing in the above result?
For example: I've 100 cities in the in-clause (hard-coded) and I want to see which cities that I passed in are not in the Address table.
You can use union to build table of cities and then minus operator.
select 'Dallas' as city from dual union all
select 'Berlin' as city from dual union all
select 'Cracow' as city from dual union all
select 'Praha' as city from dual
minus
select city from address
Instead of union you can use predefined type odcivarchar2list, which shortens syntax:
select column_value as city
from table(sys.odcivarchar2list('Dallas', 'Berlin', 'Cracow', 'Praha'))
minus
select city from address
... and instead of minus You can use joins or not in or not exists.
Test data and output of both queries:
create table address (id number, city varchar2(10));
insert into address values (1, 'Rome');
insert into address values (2, 'Dallas');
insert into address values (3, 'Cracow');
insert into address values (4, 'Moscow');
insert into address values (5, 'Liverpool');
insert into address values (6, 'Cracow');
insert into address values (7, 'Seoul');
CITY
------------
Berlin
Praha
Looks like you are looking for all addresses which is not available in the given list. You can use 'not in'
select * from Address where city not in
('a',
'b',
'c',
'd',
'e',
'f'
..
)
I have 3 tables with the following schema
create table main (
main_id int PRIMARY KEY,
secondary_id int NOT NULL
);
create table secondary (
secondary_id int NOT NULL,
tags varchar(100)
);
create table bad_words (
words varchar(100) NOT NULL
);
insert into main values (1, 1001);
insert into main values (2, 1002);
insert into main values (3, 1003);
insert into main values (4, 1004);
insert into secondary values (1001, 'good word');
insert into secondary values (1002, 'bad word');
insert into secondary values (1002, 'good word');
insert into secondary values (1002, 'other word');
insert into secondary values (1003, 'ugly');
insert into secondary values (1003, 'bad word');
insert into secondary values (1004, 'pleasant');
insert into secondary values (1004, 'nice');
insert into bad_words values ('bad word');
insert into bad_words values ('ugly');
insert into bad_words values ('worst');
expected output
----------------
1, 1000, good word, 0 (boolean flag indicating whether the tags contain any one of the words from the bad_words table)
2, 1001, bad word,good word,other word , 1
3, 1002, ugly,bad word, 1
4, 1003, pleasant,nice, 0
I am trying to use case to select 1 or 0 for the last column and use a join to join the main and secondary table, but getting confused and stuck. Can someone please help me with a query ? These tables are stored in redshift and i want query compatible with redshift.
you can use the above schema to try your query in sqlfiddle
EDIT: I have updated the schema and expected output now by removing the PRIMARY KEY in secondary table so that easier to join with the bad_words table.
You can use EXISTS and a regex comparison with \m and \M (markers for beginning and end of a word, respectively):
with
main(main_id, secondary_id) as (values (1, 1000), (2, 1001), (3, 1002), (4, 1003)),
secondary(secondary_id, tags) as (values (1000, 'very good words'), (1001, 'good and bad words'), (1002, 'ugly'),(1003, 'pleasant')),
bad_words(words) as (values ('bad'), ('ugly'), ('worst'))
select *, exists (select 1 from bad_words where s.tags ~* ('\m'||words||'\M'))::int as flag
from main m
join secondary s using (secondary_id)
select main_id, a.secondary_id, tags, case when c.words is not null then 1 else 0 end
from main a
join secondary b on b.secondary_id = a.secondary_id
left outer join bad_words c on c.words like b.tags
SELECT m.main_id, m.secondary_id, t.tags, t.is_bad_word
FROM srini.main m
JOIN (
SELECT st.secondary_id, st.tags, exists (select 1 from srini.bad_words b where st.tags like '%'+b.words+'%') is_bad_word
FROM
( SELECT secondary_id, LISTAGG(tags, ',') as tags
FROM srini.secondary
GROUP BY secondary_id ) st
) t on t.secondary_id = m.secondary_id;
This worked for me in redshift and produced the following output with the above mentioned schema.
1 1001 good word false
3 1003 ugly,bad word true
2 1002 good word,other word,bad word true
4 1004 pleasant,nice false
In SQL Server 2008 I have a table People (Id, Gender, Name).
Gender is either Male or Female. There can be many people with the same name.
I would like to write a query that displays for each gender the top 2 names
by count and their count, like this:
Male Female
Adam 23 Rose 34
Max 20 Jenny 15
I think that PIVOT might be used but all the examples I have seen display only one column for each header.
Here is an example on SQL Fiddle -- http://sqlfiddle.com/#!3/b3477/1
This uses an couple of common table expressions to separate the genders.
create table People
(
Id int,
Gender varchar(50),
Name varchar(50)
)
;
insert into People values (1, 'Male', 'Bob');
insert into People values (2, 'Male', 'Bob');
insert into People values (3, 'Male', 'Bill');
insert into People values (4, 'Male', 'Chuck');
insert into People values (5, 'Female', 'Anne');
insert into People values (6, 'Female', 'Anne');
insert into People values (7, 'Female', 'Bobbi');
insert into People values (8, 'Female', 'Jane');
with cteMale as
(
select Name as 'MaleName', Count(*) as Num, ROW_NUMBER() over(order by count(*) desc, Name) RowNum
from People
where Gender = 'Male'
group by Name
)
,
cteFemale as
(
select top 2 Name as 'FemaleName', Count(*) as Num, ROW_NUMBER() over(order by count(*) desc, Name) RowNum
from People
where Gender = 'Female'
group by Name
)
select a.MaleName, a.Num as MaleNum, b.femaleName, b.Num as FemaleNum
from cteMale a
join cteFemale b on
a.RowNum = b.RowNum
where a.RowNum <= 2
Use a windowing function. Below is a complete solution using a temporary table #people.
-- use temp db
use tempdb;
go
-- drop test table
--drop table #people;
--go
-- create test table
create table #people (my_id int, my_gender char(1), my_name varchar(25));
go
-- clear test table
delete from #people;
-- three count
insert into #people values
(23, 'M', 'Adam'),
(34, 'F', 'Rose');
go 3
-- two count
insert into #people values
(20, 'M', 'Max'),
(15, 'F', 'Jenny');
go 2
-- one count
insert into #people values
(20, 'M', 'John'),
(15, 'F', 'Julie');
go
-- grab top two by gender
;
with cte_Get_Top_Two as
(
select ROW_NUMBER() OVER(PARTITION BY my_gender ORDER BY count() DESC) AS my_window,
my_gender, my_name, count() as total
from #people
group by my_gender, my_name
)
select * from cte_Get_Top_Two where my_window in (1, 2)
go
Here is the output.
PS: You can drop my_id from the table since it does not relate to your problem but does not change solution.