What is wrong with my select statement? - sql

Here is the question, keep in mind I'm using SQL developer 3....
The Student Services department wants to know how involved each faculty member is when it comes to supplying advice to students outside of class. Supply a list of faculty IDs and the number of students that faculty member is advising. Title the output column for faculty IDs “Faculty ID”, and the output column for the student count as “NumStuds”. Produce output only if the faculty id is less than 100 and the student has a value entered in either the last name or first name field. Present the output in increasing order by faculty ID
This is what I get...
SELECT F_ID AS "Falculty ID" , COUNT S_ID AS "NumStud" FROM student
WHERE ( s_first, s_last, f_id ) IS NOT NULL
AND IS <= 100
ORDER BY F_ID ACD
Then I get error Error starting at line 329 in command:
SELECT F_ID AS "Falculty ID" , COUNT S_ID AS "NumStud" FROM student
WHERE ( s_first, s_last, f_id ) IS NOT NULL
AND IS <= 100
ORDER BY F_ID ACD
Error at Command Line:329 Column:42 Error report: SQL Error:
ORA-00923: FROM keyword not found where expected
00923. 00000 - "FROM keyword not found where expected"
PLEASE HELP!

You cannot do that since you are testing for null value. It should manually be tested.
SELECT F_ID AS "Falculty ID" , COUNT(S_ID) AS "NumStud"
FROM student
WHERE (
s_first IS NOT NULL OR
s_last IS NOT NULL OR
f_id IS NOT NULL
) AND IS <= 100
ORDER BY F_ID ASC
Second, it should ASC for Ascending not ACD

Try:
SELECT F_ID AS "Faculty ID" , COUNT(S_ID) AS "NumStud"
FROM student
WHERE COALESCE( s_first, s_last ) IS NOT NULL AND F_ID < 100
GROUP BY F_ID
ORDER BY F_ID ASC

Related

Missing expression inside sub-query statement?

I am using the infamous "CityJail" schema to answer a question "List the names of all criminals who have committed more than average number of crimes and aren’t listed as violent offenders."
Here is my code:
SELECT first, last
FROM criminals NATURAL JOIN
crimes
GROUP BY first, last
HAVING COUNT(*) > (SELECT AVG(COUNT(DISTINCT crime_id))
FROM crimes)
AND (SELECT v_status = 'N' FROM crimes)
GROUP BY first, last
);
but I get an error:
ORA-00936: missing expression
00936. 00000 - "missing expression"
*Cause:
*Action:
Error at Line: 7 Column: 22
When I change my code to:
SELECT first, last
FROM criminals NATURAL JOIN
crimes
GROUP BY first, last
HAVING COUNT(*) > (SELECT AVG(COUNT(DISTINCT crime_id))
FROM crimes)
AND v_status = 'N'
GROUP BY first, last
);
I get an error:
ORA-01787: only one clause allowed per query block
01787. 00000 - "only one clause allowed per query block"
*Cause:
*Action:
Error at Line: 8 Column: 1
What am I doing wrong?
criminals:
Name Null? Type
----------- -------- ------------
CRIMINAL_ID NOT NULL NUMBER(6)
LAST VARCHAR2(15)
FIRST VARCHAR2(10)
STREET VARCHAR2(30)
CITY VARCHAR2(20)
STATE CHAR(2)
ZIP CHAR(5)
PHONE CHAR(10)
V_STATUS CHAR(1)
P_STATUS CHAR(1)
crimes:
Name Null? Type
--------------- -------- ---------
CRIME_ID NOT NULL NUMBER(9)
CRIMINAL_ID NOT NULL NUMBER(6)
CLASSIFICATION CHAR(1)
DATE_CHARGED DATE
STATUS CHAR(2)
HEARING_DATE DATE
APPEAL_CUT_DATE DATE
DATE_RECORDED DATE
Fix Your Scalar Subquery in the Having Clause
SELECT first
, last
FROM criminals cls
NATURAL
JOIN crimes
WHERE v_status = 'N'
GROUP BY first
, last
HAVING COUNT(1) >(
SELECT AVG(COUNT(DISTINCT crime_id))
FROM crimes
GROUP BY criminal_id);
The scalar query is malformed in both of your attempts.
I moved the condition WHERE v_status = 'N' to the main query (you are only concerned about criminal whom are non-violent).

PL/SQL How to obtain count of the table I am querying from? How to use NEW with COUNT?

below is my code that will trigger when a new row in Trip is inserted. Then it will update on the column totalTripMade in the Driver table.
CREATE OR REPLACE TRIGGER UPDATE_TOTAL_TRIPS_MADE
AFTER INSERT
ON TRIP
FOR EACH ROW
DECLARE
tripsDone NUMBER(6);
driverL# NUMBER(12);
BEGIN
--Find the L# of the Driver performing the INSERT into the Trip table
SELECT D.L# INTO driverL#
FROM DRIVER D
WHERE D.L# =: NEW.L#;
--Find the number of trips done by the driver (Error occured here)
SELECT COUNT(T#) INTO tripsDone
FROM TRIP T
WHERE NEW.L# =: driverL#;
--Then update the totaltripmade by the driver
UPDATE
DRIVER
SET
totalTripMade = tripsDone
WHERE
L# = driverL#;
END UPDATE_TOTAL_TRIPS_MADE;
/
However, there is compilation error due to not able to query TRIP until the trigger is completed.
So, I tried changing the select count statement to like that:
SELECT COUNT(*) INTO tripsDone
FROM TRIP T
WHERE driverL# =: NEW.L#;
But it did not work too. I am not sure how can I work around to get the total number of rows in the table Trip with the driverL# that triggered the trigger.
For solving this problem, you should probably NOT use a trigger. Just write a query for counting the "trips". Example:
Tables
create table driver (
id number generated always as identity start with 1 primary key
, surname varchar2( 50 )
-- , ttm number -- total trips made: redundant!
) ;
create table trip (
id number generated always as identity start with 1000 primary key
, from_ varchar2( 50 )
, to_ varchar2( 50 )
, driver number references driver( id )
) ;
Test data: DRIVER
insert into driver( surname )
select
'driver_' || to_char( level )
from dual
connect by level <= 10 ;
select * from driver ;
ID SURNAME
1 driver_1
2 driver_2
3 driver_3
4 driver_4
5 driver_5
6 driver_6
7 driver_7
8 driver_8
9 driver_9
10 driver_10
Test data: TRIP
begin
for i in 1 .. 10
loop
insert into trip ( from_, to_, driver ) values (
'departure_' || to_char( i )
, 'arrival_' || to_char( i )
, mod( i, 3 ) + 1
) ;
end loop ;
commit ;
end ;
/
SQL> select * from trip ;
ID FROM_ TO_ DRIVER
1000 departure_1 arrival_1 2
1001 departure_2 arrival_2 3
1002 departure_3 arrival_3 1
1003 departure_4 arrival_4 2
1004 departure_5 arrival_5 3
1005 departure_6 arrival_6 1
1006 departure_7 arrival_7 2
1007 departure_8 arrival_8 3
1008 departure_9 arrival_9 1
1009 departure_10 arrival_10 2
Query: find the "trip count"
select D.id, D.surname, count(*) as trips_made
from driver D
join trip T on D.id = T.driver
group by D.id, D.surname ;
-- result
ID SURNAME TRIPS_MADE
1 driver_1 3
2 driver_2 4
3 driver_3 3
However, if you want to learn about triggers, you will find that a "row level trigger" will give you "mutating table" errors. When using a "table level" trigger, you cannot reference :NEW and :OLD. What you need is a COMPOUND TRIGGER. You can find numerous discussions about this problem. Apart from consulting the Oracle PL/SQL documentation, it may be worth your while looking that the examples written by T Hall eg here, and S Feuerstein eg here.
The dbfiddle here contains the code used for this answer, and a third example table called DRIVERV2 plus examples of the above mentioned trigger types (and typical error messages).

PostgreSQL 9.3: Pivot table query

I want to show the pivot table(crosstab) for the given below table.
Table: Employee
CREATE TABLE Employee
(
Employee_Number varchar(10),
Employee_Role varchar(50),
Group_Name varchar(10)
);
Insertion:
INSERT INTO Employee VALUES('EMP101','C# Developer','Group_1'),
('EMP102','ASP Developer','Group_1'),
('EMP103','SQL Developer','Group_2'),
('EMP104','PLSQL Developer','Group_2'),
('EMP101','Java Developer',''),
('EMP102','Web Developer','');
Now I want to show the pivot table for the above data as shown below:
Expected Result:
Employee_Number TotalRoles TotalGroups Available Others Group_1 Group_2
---------------------------------------------------------------------------------------------------
EMP101 2 2 1 1 1 0
EMP102 2 2 1 1 1 0
EMP103 1 2 1 0 0 1
EMP104 1 2 1 0 0 1
Explanation: I want to show the Employee_Number, the TotalRoles which each employee has,
the TotalGroups which are present to all employees, the Available shows the employee available
in how many groups, the Others have to show the employee is available in other's also for which
the group_name have not assigned and finally the Group_Names must be shown in the pivot format.
SELECT * FROM crosstab(
$$SELECT grp.*, e.group_name
, CASE WHEN e.employee_number IS NULL THEN 0 ELSE 1 END AS val
FROM (
SELECT employee_number
, count(employee_role)::int AS total_roles
, (SELECT count(DISTINCT group_name)::int
FROM employee
WHERE group_name <> '') AS total_groups
, count(group_name <> '' OR NULL)::int AS available
, count(group_name = '' OR NULL)::int AS others
FROM employee
GROUP BY 1
) grp
LEFT JOIN employee e ON e.employee_number = grp.employee_number
AND e.group_name <> ''
ORDER BY grp.employee_number, e.group_name$$
,$$VALUES ('Group_1'::text), ('Group_2')$$
) AS ct (employee_number text
, total_roles int
, total_groups int
, available int
, others int
, "Group_1" int
, "Group_2" int);
SQL Fiddle demonstrating the base query, but not the crosstab step, which is not installed on sqlfiddle.com
Basics for crosstab:
PostgreSQL Crosstab Query
Special in this crosstab: all the "extra" columns. Those columns are placed in the middle, after the "row name" but before "category" and "value":
Pivot on Multiple Columns using Tablefunc
Once again, if you have a dynamic set of groups, you need to build this statement dynamically and execute it in a second call:
Selecting multiple max() values using a single SQL statement
You can use the crosstab function for this.
First of all you need to add the tablefunc extension if you haven't already:
CREATE EXTENSION tablefunc;
The crosstab functions require you to pass it a query returning the data you need to pivot, then a list of the columns in the output. (In other ways "tell me the input and the output format you want"). The sort order is important!
In your case, the input query is quite complicated - I think you need to do a load of separate queries, then UNION ALL them to get the desired data. I'm not entirely sure how you calculate the values "TotalGroups" and "Available", but you can modify the below in the relevant place to get what you need.
SELECT * FROM crosstab(
'SELECT employee_number, attribute, value::integer AS value FROM (with allemployees AS (SELECT distinct employee_number FROM employee) -- use a CTE to get distinct employees
SELECT employee_number,''attr_0'' AS attribute,COUNT(distinct employee_role) AS value FROM employee GROUP BY employee_number -- Roles by employee
UNION ALL
SELECT employee_number,''attr_1'' AS attribute,value from allemployees, (select count (distinct group_name) as value from employee where group_name <> '''') a
UNION ALL
SELECT employee_number,''attr_2'' AS attribute, COUNT(distinct group_name) AS value FROM employee where group_name <> '''' GROUP BY employee_number -- Available, do not know how this is calculate
UNION ALL
SELECT a.employee_number, ''attr_3'' AS attribute,coalesce(value,0) AS value FROM allemployees a LEFT JOIN -- other groups. Use a LEFT JOIN to avoid nulls in the output
(SELECT employee_number,COUNT(*) AS value FROM employee WHERE group_name ='''' GROUP BY employee_number) b on a.employee_number = b.employee_number
UNION ALL
SELECT a.employee_number, ''attr_4'' AS attribute,coalesce(value,0) AS value FROM allemployees a LEFT JOIN -- group 1
(SELECT employee_number,COUNT(*) AS value FROM employee WHERE group_name =''Group_1'' GROUP BY employee_number) b on a.employee_number = b.employee_number
UNION ALL
SELECT a.employee_number, ''attr_5'' AS attribute,coalesce(value,0) AS value FROM allemployees a LEFT JOIN -- group 2
(SELECT employee_number,COUNT(*) AS value FROM employee WHERE group_name =''Group_2'' GROUP BY employee_number) b on a.employee_number = b.employee_number) a order by 1,2')
AS ct(employee_number varchar,"TotalRoles" integer,"TotalGroups" integer,"Available" integer, "Others" integer,"Group_1" integer, "Group_2" integer)

Insert in table, Sequence.nextval not working

I have the following 3 tables,
Data_Excel contains the names, address, city and source of person;
Person table has the name and ID;
I need to insert into person_location the address source, address, city and ID...
I am using the following query :
CREATE SEQUENCE seq
START WITH 6571
MINVALUE 6571
INCREMENT BY 1
CACHE 100
INSERT INTO Person (id,Name,source)
Select (seq.nextval),p_name,source
FROM Data_Excel
WHERE P_Name NOT IN
(SELECT name FROM Person)
GROUP BY P_Name,P_Address,P_city,Source
HAVING count(*) < 2;
but I get the following error.
I am using seq because ID is the primary key in persons but its not auto incrementing. I also tried that but there was an error :
02287. 00000 - "sequence number not allowed here"
*Cause: The specified sequence umber (CURRVAL or NEXTVAL) is inappropriate
here in the statement.
*Action: emove the sequence number.
Try moving the sequence out of the grouping query:
INSERT INTO Person (id,Name,source)
SELECT seq.nextval, p_name,source FROM (
Select p_name,source
FROM Data_Excel
WHERE P_Name NOT IN
(SELECT name FROM Person)
GROUP BY P_Name,P_Address,P_city,Source
HAVING count(*) < 2
);

Join logic from two separate tables in sql

We returned a list of cardID's after a query and those cardID's belong to two tables Student and Personnel. So how can I join those cardID's with Student and Personnel so I can return a table that shows name of Student and Personnel according to cardID's?
Personnel table:
PERSONNELID NUMBER(9,0)
PERSONNELNAME VARCHAR2(20)
PERSONNELSURNAME VARCHAR2(20)
PERSONNELJOB VARCHAR2(40)
PERSONNELCARDID NUMBER(4,0)
Student table:
STUDENTID NUMBER(9,0)
STUDENTNAME VARCHAR2(20)
STUDENTSURNAME VARCHAR2(20)
STUDENTDEPT VARCHAR2(40)
STUDENTFACULTY VARCHAR2(20)
STUDENTCARDID NUMBER(4,0)
CardID table
CARDID NUMBER(4,0)
USERTYPE VARCHAR2(20)
CHARGE NUMBER(3,2)
CREDIT NUMBER(4,2)
PaymentDevice table:
ORDERNO NUMBER
PAYDEVIP NUMBER(8,0)
PAYDEVDATE DATE No
PAYDEVTIME VARCHAR2(8)
CHARGEDCARDID NUMBER(9,0)
MEALTYPE VARCHAR2(10)
I tried to return first 10 person's name and surname that eat at cafeteria on 27/12/2012
SELECT C.CARDID
FROM CARD C, PAYMENTDEVICE P
WHERE P.ORDERNO
BETWEEN (SELECT MIN(ORDERNO)
FROM PAYMENTDEVICE
WHERE PAYDEVDATE='27/12/2012') AND (SELECT MIN(ORDERNO)
FROM PAYMENTDEVICE
WHERE PAYDEVDATE='27/12/2012')+10 AND C.CARDID=P.CHARGEDCARDID;
Our orderNo isn't reset everyday but keeps increasing so we found the min orderNo that day and add 10 to this value to find first 10 person who eat on that day between those order numbers.
So what return from this query:
CARDID
1005
1000
1002
1003
1009
2000
2001
1007
2002
1004
1006
and those some of those cardId (start with 1) are studentCardId and some of them (starts with 2) are PersonnelCardId. So how can I match and write names accordingly?
SELECT *
FROM Personel p INNER JOIN Student s
ON p.PersonnelCardId = s.StudentCardId
INNER JOIN ReturnedQuery rq
ON rq.CardId = p.PersonnelCardId
updated:
SELECT p.PersonnelName, rq.CardId
FROM Personel p INNER JOIN ReturnedQuery rq
ON rq.CardId = p.PersonnelCardId
UNION
SELECT s.StudentName, rq.Cardid
FROM Student s INNER JOIN ReturnedQuery rq
ON s.StudentCardId = rq.Cardid
Your original query is actually pretty fragile. I'd rewrite it like so (and added the needed joins):
WITH First_Daily_Purchase as (SELECT chargedCardId,
MIN(payDevTime) as payDevTime,
MIN(orderNo) as orderNo
FROM PaymentDevice
WHERE payDevDate >=
TO_DATE('2012-12-27', 'YYYY-MM-DD')
AND payDevDate <
TO_DATE('2012-12-28', 'YYYY-MM-DD')
GROUP BY chargedCardId),
First_10_Daily_Purchasers as (SELECT chargedCardId
FROM (SELECT chargedCardId,
RANK() OVER(ORDER BY payDevTime,
orderNo) as rank
FROM First_Daily_Purchase) a
WHERE a.rank < 11)
SELECT a.chargedCardId, b.personnelName, b.personnelSurname
FROM First_10_Daily_Purchasers a
JOIN Personnel b
ON b.personnelCardId = a.chargedCardId
UNION ALL
SELECT a.chargedCardId, b.studentName, b.studentSurname
FROM First_10_Daily_Purchasers a
JOIN Student b
ON b.studentCardId = a.chargedCardId
(Have a working SQL Fiddle - generally bullet-proofing this took me a while.)
This should get you the first 10 people who made a purchase (not the first 11 purchases, which is what you were actually getting). This of course assumes that payDevTime is actually stored in a sortable format (if it isn't you have bigger problems than this query not working quite right).
That said, there's a number of troubling things about your schema design.