SQL split column - sql

I made an easy exemple to understand better what is my question.
So, i have 2 table and i have a selection based on inner join which is into a selection.
create table students(
id_student number,
name_student varchar2(15),
id_advisor number,
money number
);
insert into students values(1, 'Student_1', 1, 100);
insert into students values(2, 'Student_2', 8,-200);
insert into students values(4, 'Student_4', 7, 256);
insert into students values(5, 'Student_5', 3, -305);
----------------
create table advisors(
id_advisor number,
name_advisor varchar2(15)
);
insert into advisors values(1, 'advisor_1');
insert into advisors values(3, 'advisor_3');
insert into advisors values(5, 'advisor_5');
------------------------------------------
SELECT name_advisor, money as money_pozitive
FROM(
select name_student, name_advisor, money from students
inner join advisors on students.id_advisor = advisors.id_advisor)
WHERE money > 0
With this code i have the following result:
name_advisor | money_pozitive
------------------------------------
advisor_1 | 100
My question is, how I add an extra column named money_negative with of course negative values ? like this:
name_advisor | money_pozitive | money_negative
---------------------------------------------------------
advisor_1 | 100 | -305

Just use case:
select name_student, name_advisor,
(case when money > 0 then money end) as money_positive,
(case when money < 0 then money end) as money_negative
from students s inner join
advisors a
on s.id_advisor = a.id_advisor;
Notes:
A subquery is not necessary.
Use table aliases. These make a query easier to write and to read.
If you have multiple table names, it is a good habit to qualify all column names (i.e., use the table aliases for the column names).
And you don't need a where clause.

Related

SQL query for querying counts from a table

The prompt is to form a SQL query.
That finds the students name and ID who attend all lectures having ects more than 4.
The tables are
CREATE TABLE CLASS (
STUDENT_ID INT NOT NULL,
LECTURE_ID INT NOT NULL
);
CREATE TABLE STUDENT (
STUDENT_ID INT NOT NULL,
STUDENT_NAME VARCHAR(255),
PRIMARY KEY (STUDENT_ID)
)
CREATE TABLE LECTURE (
LECTURE_ID INT NOT NULL,
LECTURE_NAME VARCHAR(255),
ECTS INT,
PRIMARY KEY (LECTURE_ID)
)
I came up with this query but this didn't seem to work on SQLFIDDLE. I'm new to SQL and this query has been a little troublesome for me. How would you query this?
SELECT STUD.STUDENT_NAME FROM STUDENT STUD
INNER JOIN CLASS CLS AND LECTURE LEC ON
CLS.STUDENT_ID = STUD.STUDENT_ID
WHERE LEC.CTS > 4
How do I fix this query?
UPDATE
insert into STUDENT values(1, 'wick', 20);
insert into STUDENT values(2, 'Drake', 25);
insert into STUDENT values(3, 'Bake', 42);
insert into STUDENT values(4, 'Man', 5);
insert into LECTURE values(1, 'Math', 6);
insert into LECTURE values(2, 'Prog', 6);
insert into LECTURE values(3, 'Physics', 1);
insert into LECTURE values(4, '4ects', 4);
insert into LECTURE values(5, 'subj', 4);
insert into SCLASS values(1, 3);
insert into SCLASS values(1, 2);
insert into SCLASS values(2, 3);
insert into SCLASS values(3, 1);
insert into SCLASS values(3, 2);
insert into SCLASS values(3, 3);
insert into SCLASS values(4, 4);
insert into SCLASS values(4, 5);
The following approach might get the job done.
It works by generating two subqueries :
one that counts how many lectures whose ects is greater than 4 were taken by each user
another that just counts the total number of lectures whose ects is greater than 4
Then, the outer query filters in users whose count reaches the total :
SELECT x.student_id, x.student_name
FROM
(
SELECT s.student_id, s.student_name, COUNT(DISTINCT l.lecture_id) cnt
FROM
student s
INNER JOIN class c ON c.student_id = s.student_id
INNER JOIN lecture l ON l.lecture_id = c.lecture_id
WHERE l.ects > 4
GROUP BY s.student_id, s.student_name
) x
CROSS JOIN (SELECT COUNT(*) cnt FROM lecture WHERE ects > 4 ) y
WHERE x.cnt = y.cnt ;
As GMB already said in their answer: count required lections and compare with those taken per student. Here is another way to write such query. We outer join classes to all lectures with ECTS > 4. Analytic window functions allow us to aggregate by two different groups at the same time (here: all rows and student's rows).
select *
from student
where (student_id, 0) in -- 0 means no gap between required and taken lectures
(
select
student_id,
count(distinct lecture_id) over () -
count(distinct lecture_id) over (partition by c.student_id) as gap
from lecture l
left join class c using (lecture_id)
where l.ects > 4
);
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=74371314913565243863c225847eb044
You can try the following query.
SELECT distinct
STUD.STUDENT_NAME,
STUD.STUDENT_ID
FROM STUDENT STUD
INNER JOIN CLASS CLS ON CLS.STUDENT_ID = STUD.STUDENT_ID
INNER JOIN LECTURE LEC ON LEC.LECTURE_ID=CLS.LECTURE_ID
where LEC.ECTS > 4 group by STUD.STUDENT_ID,STUD.STUDENT_NAME
having COUNT(STUD.STUDENT_ID) =(SELECT COUNT(*) FROM LECTURE WHERE ECTS > 4)

sql select sum conditions

I'm studying sql (by myself) and I would like to know how I would do for these examples:
1- i'd create this 3 tables bellow:
CREATE TABLE Business (
Id INT,
Category INT,
Business_Name VARCHAR(30),
City_Id INT,
Billing INT
);
INSERT INTO business (Id, Category, Business_Name, City_Id, Billing) VALUES(1, 1, 'Bread', 1, 50);
INSERT INTO business (Id, Category, Business_Name, City_Id, Billing) VALUES(2, 2, 'Oreo', 2, 10);
INSERT INTO business (Id, Category, Business_Name, City_Id, Billing) VALUES(3, 2, 'Pizza', 3, 15);
INSERT INTO business (Id, Category, Business_Name, City_Id, Billing) VALUES(4, 2, 'Beer', 4, 25);
INSERT INTO business (Id, Category, Business_Name, City_Id, Billing) VALUES(5, 1, 'Steak', 1, 80);
CREATE TABLE City (
Id INT,
City_Name VARCHAR(30)
);
INSERT INTO City (Id, City_Name) VALUES(1, 'Paris');
INSERT INTO City (Id, City_Name) VALUES(2, 'New York');
INSERT INTO City (Id, City_Name) VALUES(3, 'Tokio');
INSERT INTO City (Id, City_Name) VALUES(4, 'Vancouver');
INSERT INTO City (Id, City_Name) VALUES(5, 'Cairo');
CREATE TABLE Category (
Id INT,
Category_Name VARCHAR(30)
);
INSERT INTO Category (Id, Category_Name) VALUES(1, 'Bar');
INSERT INTO Category (Id, Category_Name) VALUES(2, 'Pub');
INSERT INTO Category (Id, Category_Name) VALUES(3, 'Pizza');
2- I want to make these SQL queries:
a) Total Value of Billing (Billing) all stores, like this table:
-----------------------
|Business_Name | Total |
|--------------+-------|
|Total | 180 |
------------------------
b) All Total Billing by Category_Name like this table:
-------------------
|Category | Total |
|---------+-------|
|Bar | 130 |
|---------+-------|
|Pub | 50 |
|---------+-------|
|Pizza | 5 |
----------+--------
c)List the Business_Name with min billing, showing the: Category_Name, Business_Name, and Billing like this table:
----------------------------------------
|Category_Name | Business_Name | Total |
|--------------+---------------+-------|
|Pub | Beer | 5 |
|--------------+---------------+--------
d) All Total of Billing by City, showing the: Category_Name, Business_Name, City_Name and Billing like this table
--------------------------
|City | Total |
|----------------+-------|
|Cairo | 0 |
|----------------+-------|
|New York | 10 |
|----------------+-------|
|Paris | 130 |
|----------------+-------|
|Tokio | 15 |
-----------------+--------
|Vancouver | 25 |
-----------------+--------
Any body with a little more knowledge that could be help me, please? =)
First thing is first, all of these are basic queries and i have to point out that a simple google search for tutorials(ex1, ex2, ex3) would have answered most of these. as we are here to provide help and guidance i hope you take it to heart and read the tutorials before going over the answers.
with that said to be able to help you out i will walk through each query and provide an overview of what is happening.
a) you need an aggregate operation here to sum up the values. You would use the sum key word. normally you need a group by, but in this case since we only have a hard coded column with the word "Total" in it, it is not required. we also give each of the columns an alias as per your table. this is after the column name.
select 'Total' as business_Name,
sum(billing) Total
from business
b) This one is almost an exact copy of a, but requires a grouping. in this case you have to use the group by key word for all columns that are not in aggregates. in this case it is only the category name. it is good practice to not use the ordinal position in group by and order by statements, you should always spell out the columns you are using when able.
select c.category_name,
sum(billing) total
from business b
inner join category c
on b.category = c.id
group by c.category_name
c) We continue to build onto the query and add another column in the select statement and then add a column to the group by to allow grouping.
select c.category_name,
b.business_name,
sum(billing) total
from business b
inner join category c
on b.category = c.id
group by c.category_name, b.business_name
d) For this query its very similar to b, but instead of category_name we do a join on city with city id.
select c.city_name
,sum(billing) as total
from business b
inner join city c on c.id = b.city_id
group by c.city_name
with all of this said, several of your examples do not match your expected output. but these queries do match the expected output with the data you provided.
I really do recommend going through some tutorials to grasp the basics of sql better.
Here is answer to one of the queries. But I'd recommend you to read online basic sql tutorials and you will be able to write them yourself easily.
b)
select c.category_name
,sum(billing)
from business b
join category c
on b.category = c.id
group by 1

Re-write oracle query to avoid multiple table scan

I hope everyone is fine and learning more. I need some advice on select statement and on its fine tuning. I am using Oracle 11gR2. Please find below table and data scripts.
create table employee (emp_id number, emp_name varchar2(50), manager_id number);
create table department (dept_id number, dept_name varchar2(50), emp_name varchar2(50), manager_level varchar2(20));
create table manager_lookup (manager_level_id number, manager_level varchar2(20));
insert into employee values (1, 'EmpA',3);
insert into employee values (2, 'EmpB',1);
insert into employee values (3, 'EmpC',1);
insert into employee values (4, 'EmpD',2);
insert into employee values (5, 'EmpE',1);
insert into employee values (6, 'EmpF',3);
insert into department values (1, 'DeptA','EmpD','Level3');
insert into department values (2, 'DeptB','EmpC','Level2');
insert into department values (3, 'DeptC','EmpA','Level1');
insert into department values (4, 'DeptD','EmpF','Level1');
insert into department values (5, 'DeptD','EmpA','Level3');
insert into department values (6, 'DeptA',NULL,'Level3');
insert into manager_lookup values (1, 'Level1');
insert into manager_lookup values (2, 'Level2');
insert into manager_lookup values (3, 'Level3');
commit;
Below query is returning me dept_id by passing some emp_name. I need those dept_id where manager_level is same as emp_name passed but don't need to have that same emp_name in result data set.
SELECT b.dept_id
FROM (SELECT DISTINCT manager_level
FROM department dpt
WHERE emp_name = 'EmpA'
and emp_name is not null) a,
department b
WHERE a.manager_level = b.manager_level
AND NVL (b.emp_name, 'ABC') <> 'EmpA';
Above query is returning me data set as below:
dept_id
--------
1
4
6
I want same result set but need to rewrite above query in a way to avoid department table scan two times. This is just sample query but in real time scanning big table two times is giving performance issues. I want to re-write this query in a way to perform better and avoid same table scan two times.
Can you please help on providing your wonderful suggestions or solutions? I will really appreciate all responses.
Thank you for going over the question.
If you want your query to be more efficient, then use indexes:
create index idx_department_name_level on department(emp_name, manager_level)
and
create index idx_department_name_level on department(manager_level, emp_name, dept_id)
also, you have a redundant null check which may be avoiding indexes...
SELECT b.dept_id
FROM (SELECT manager_level
FROM department dpt
WHERE emp_name = 'EmpA') a,
department b
WHERE a.manager_level = b.manager_level
AND NVL (b.emp_name, 'ABC') <> 'EmpA';
post your explain plan for more help
This should work:
SELECT a.*
FROM
(
SELECT d.*,
SUM(CASE WHEN emp_name = 'EmpA' THEN 1 ELSE 0 END)
OVER (PARTITION BY manager_level) AS hits
FROM department d
) a
WHERE hits > 0
AND NVL(emp_name, 'Dummy') <> 'EmpA'
ORDER BY dept_id
;
The query does the following:
Calculate how many times EmpA appears in a given manager_level
Keep all records with a manager_level that has at least one occurrence of EmpA in it
Excluding the EmpA records themselves
SQL Fiddle of the query in action: http://sqlfiddle.com/#!4/a9e03/7
You can verify that the execution plan contains only one full table scan.

Join all rows in table with first matched row in other table [duplicate]

This question already has answers here:
SQL join to correlated subquery where tables are related by overlapping ranges
(2 answers)
Closed 8 years ago.
I have 2 tables, and i want table1 left join table2, idea is to show all table1's rows and for each table1's row, i search records in all table2 until the 1st matched value.
So results' row number = talbe1's row number, just add table2's 1st matched value, but here i get results' row number > talbe1's row number
It depends on your table structures and how you join them. I can think of an example that the row number of the result > table 1 row number.
create table staff (
staff_id int,
name varchar(100)
)
;
create table stationery (
s_id int,
name varchar(100),
staff_id int
)
;
insert into staff values (1, 'Peter');
insert into staff values (2, 'Sally');
insert into stationery values (1, 'ruler', 1);
insert into stationery values (2, 'pencil', 1);
insert into stationery values (3, 'pencil', 2);
select *
from staff s1
left join stationery s2 on s1.staff_id = s2.staff_id ;
STAFF_ID NAME S_ID
1 Peter 11 Peter 22 Sally 3

Insert query issue- with foreign key

I am new to SQL. Need a help from you guys :)
I am building a java appl and stuck in one of the scenario for insert with foreign key. Suppose I have 2 tables Employee_Type and Employee:
Table Employee_Type
| idType | position |
| -------- | -------------- |
| 1| Manager|
Table Employee
empId
EmpName
emp_type
FK (emp_type) reference Employee_type(idType)
Now values in Employee_Type
1,
Manager
I am inserting manually into Employee Table
INSERT INTO
employee (empId, name, emp_type)
VALUES
(
10, 'prashant', 1
)
Here in above insert I am inserting manually emp_type which is FK . My question, is there any way to insert FK value automatically using select like below example?
INSERT INTO
employee(empId, name, emp_type)
VALUES
(
10, 'prashant',
(
SELECT
idType
FROM
Employee_type,
employee
WHERE
employee.emp_type = employee_type.idtype
)
)
You don't specify your RDBMS and the syntax may therefore differ, but you should be able to restructure the statement to use literal values in an INSERT INTO ... SELECT format:
INSERT INTO employee (empId,name,emp_type)
SELECT
/* Build a SELECT statement which includes the static values as literals */
'10' AS empId,
'prashant' AS name,
/* and the idType column */
idType
FROM Employee_type,employee
WHERE employee.emp_type=employee_type.idtype
Note that without anything else in the WHERE clause, the above will insert one row into employee for every row matched by the SELECT statement.