I am a bit obsessed now with this quiz, it seems I cant find a way out... hehe .
Let's see - we have two tables:
actor (id, salary, bonus)
info (id, name, surname)
The question is, to create a procedure so that it shows salary, bonus, name, surname of the actor and additionally his final paycheck = (bonus + salary) .
To begin with I tried this: (just to show the salary, bonus, name, surname and not the total paycheck, but it failed big time).
create or replace procedure show_things AS
BEGIN
Select a.id, a.salary, a.bonus
From actor a
where a.id in
(select i_id, i.name, i.surname
from info i
where i_id = a_id);
END;
I mean, is it possible to show things using a procedure ? I really can't understand this question.
I don't really know which version of SQL that you're using, but essentially, you're going to want to join two tables and query from them, like so:
SELECT
actor.salary,
actor.bonus,
info.name,
info.surname,
actor.salary + actor.bonus AS total_pay
FROM actor INNER JOIN info;
Done :-)
It's a simple join:
SELECT
a.id,
a.salary,
a.bonus,
i.name,
i.surname,
a.salary + a.bonus AS final_paycheck
FROM
actor a INNER JOIN info i
ON
a.id= i.id
Your procedure won't compile as-is. You'll (at least) need to define some local variables, and use the INTO clause to select into them.
You can then output the result of your procedure using dbms_output.put_line(), assuming that you have SERVEROUTPUT turned on.
In addition, you can't use the IN operator with a different number of columns (1) to that in the subquery it references (3). You should use a simple JOIN as others have suggested.
Please see the sample in the PL/SQL documentation.
You can use pipepline function to achieve this.
-- drop type t_actor_tab;
-- drop type t_actor_obj;
-- drop function get_actor_ptf;
-- drop table ACTOR;
-- drop table info;
CREATE TABLE ACTOR(ID NUMBER, SAL NUMBER, BONUS NUMBER);
INSERT INTO ACTOR(ID, SAL, BONUS) VALUES(1, 1200, 120);
INSERT INTO ACTOR(ID, SAL, BONUS) VALUES(2, 1300, 240);
INSERT INTO ACTOR(ID, SAL, BONUS) VALUES(3, 1500, 120);
CREATE TABLE INFO(ID NUMBER, NAME VARCHAR2(30), SURNAME VARCHAR2(30));
INSERT INTO INFO(ID, NAME, SURNAME) VALUES(1, 'ABC', 'abc');
INSERT INTO INFO(ID, NAME, SURNAME) VALUES(2, 'xyz', 'xyz');
INSERT INTO INFO(ID, NAME, SURNAME) VALUES(3, 'MNO', 'mno');
CREATE TYPE t_actor_obj AS OBJECT (
id NUMBER
,sal NUMBER
,bonus NUMBER
,final_paycheck NUMBER
);
CREATE TYPE t_actor_tab IS TABLE OF t_actor_obj;
CREATE OR REPLACE FUNCTION get_actor_ptf
RETURN t_actor_tab PIPELINED AS
CURSOR get_details_cur
IS
SELECT a.id
,a.sal
,a.bonus
,(a.sal+a.bonus) final_paycheck
From actor a
where EXISTS(SELECT 1 FROM info i WHERE i.id = a.id);
v_details_row get_details_cur%ROWTYPE;
BEGIN
OPEN get_details_cur;
LOOP
FETCH get_details_cur INTO v_details_row;
EXIT WHEN get_details_cur%NOTFOUND;
PIPE ROW(t_actor_obj (v_details_row.id,v_details_row.sal,v_details_row.bonus,v_details_row.final_paycheck));
END LOOP;
CLOSE get_details_cur;
RETURN;
END;
/
SELECT * FROM TABLE(get_actor_ptf);
ID SAL BONUS FINAL_PAYCHECK
---------- ---------- ---------- --------------
1 1200 120 1320
2 1300 240 1540
3 1500 120 1620
CREATE TABLE ACTOR(ID BIGINT, SAL BIGINT, BONUS BIGINT);
INSERT INTO ACTOR(ID, SAL, BONUS) VALUES(1, 1200, 120), (2, 1300, 240),(3, 1500, 120);
CREATE TABLE INFO(ID BIGINT, NAME VARCHAR(30), SURNAME VARCHAR(30));
INSERT INTO INFO(ID, NAME, SURNAME) VALUES(1, 'ABC', 'abc'), (2, 'xyz', 'xyz'), (3, 'MNO', 'mno')
SELECT * FROM ACTOR
SELECT * FROM INFO
CREATE OR REPLACE PROCEDURE SP1()
LANGUAGE SQL
DYNAMIC RESULT SETS 1
BEGIN
DECLARE DISPLAY CURSOR WITH RETURN FOR
SELECT X.ID, Y.NAME, Y.SURNAME, X.SAL, X.BONUS, X.SAL + X. BONUS AS TOTAL FROM ACTOR X JOIN INFO Y ON X.ID = Y. ID ;
OPEN DISPLAY;
END#
CALL SP1()
Related
I have a table in Data warehouse.
Create table customer
(
id int,
name varchar(30),
address varchar(50)
);
let data in table
insert into Customer values(1, 'Smith', 'abc,def, lkj');
insert into Customer values(2, 'James', 'pqr,lmn');
i want to split the table address column and insert new row if we have many values. Like
1 Smith abc
1 Smith def
1 Smith lkj
2 James pqr
2 James lmn
i have data of 100000 recrds, please help me in this regards.
You can use string_split() and adjust the insert statement:
insert into Customer (id, name, address)
select v.id, v.name, s.value
from (values (1, 'Smith', 'abc,def,lkj')
) v(id, name, address) cross apply
string_split(v.address, ',') s;
You might also want to add a check constraint on address so it does not contain a comma.
You would load from a staging table to the final table by doing:
insert into Customer (id, name, address)
select t.id, t.name, s.value
from staging t cross apply
string_split(v.address, ',') s;
So I have been trying to find the best way to re-write a large chunk of SQL in plan script, in form of
WITH
A AS (...<SUB_QA>...),
B AS (...<SUB_QB>...),
C AS (...<SUB_QC>...),
...
SELECT ... FROM
A
LEFT JOIN B
LEFT JOIN C
LEFT JOIN ...
ON ....
into a FUNCTION. This is mainly to facilitate the reuse the same logic represented by that big chunk in multiple places.
Contraint 1: can only use RECORD instead of creating customized TYPE;
Contraint 2: have to keep the content of the those subqueries (e.g.
, etc) under WITH clause as they are, since each is
considerably complex.
So far, I only came up with the following, as a simplified example.
It involves putting the WITH clause in the cursor loop
But does it run the WITH clause in each loop, which would be a big worry in term of performance? When written in function form, and when I run it with SqlDeveloper's 'Explain Plan' function, it doesn't reveal much helpful information at all.
Is there better/cleaner/more performant way to do this?
SQL to create data:
--------PERSON table------------
DROP TABLE Test_Persons;
CREATE TABLE Test_Persons (
PersonID int,
LastName varchar2(255),
FirstName varchar2(255)
);
INSERT INTO Test_Persons
(PersonID,LastName,FirstName)
values(1,'LN_1','FN_1');
INSERT INTO Test_Persons
(PersonID,LastName,FirstName)
values(2,'LN_2','FN_2');
INSERT INTO Test_Persons
(PersonID,LastName,FirstName)
values(3,'LN_21','FN_2');
--------Salary table------------
DROP TABLE TEST_SALARY_A;
CREATE TABLE TEST_SALARY_A ( -- no 'OR REPLACE' for ORACLE
SalaryID int,
PersonID int,
Amount int,
Tax int,
Bank varchar2(20)
);
INSERT INTO TEST_SALARY_A
(SalaryID, PersonID, Amount, Tax, Bank)
VALUES
(1, 1, 1000, 300, 'BOA1');
INSERT INTO TEST_SALARY_A
(SalaryID, PersonID, Amount, Tax, Bank)
VALUES
(2, 2, 2000, 600, 'JP1');
INSERT INTO TEST_SALARY_A
(SalaryID, PersonID, Amount, Tax, Bank)
VALUES
(3, 3, 3000, 900, 'TD1');
--------Address table------------
DROP TABLE TEST_ADDRESS_A;
CREATE TABLE TEST_ADDRESS_A (
AddressID int,
PersonID int,
Address varchar2(255)
);
INSERT INTO TEST_ADDRESS_A
(AddressID, PersonID, Address)
VALUES
(1, 1, 'address1');
INSERT INTO TEST_ADDRESS_A
(AddressID, PersonID, Address)
VALUES
(2, 2, 'address2');
INSERT INTO TEST_ADDRESS_A
(AddressID, PersonID, Address)
VALUES
(3, 3, 'address3');
commit;
Original SQL in Chunk:
------------------Original--------------------
WITH
TEST_JOINED_1 AS (
SELECT
tps.PERSONID,
tps.LASTNAME,
tsd.ADDRESS
FROM TEST_PERSONS tps
LEFT JOIN TEST_ADDRESS_A tsd ON tps.personid = tsd.personid WHERE tps.LASTNAME = 'LN_1'
),
TEST_JOINED_2 AS (
SELECT
tps.PERSONID,
tsl.BANK,
tsl.TAX
FROM TEST_PERSONS tps
LEFT JOIN TEST_SALARY_A tsl ON tps.personid = tsl.personid WHERE tps.LASTNAME = 'LN_1'
)
SELECT tj1.PERSONID as tj1_ID, tj1.LASTNAME, tj1.ADDRESS, tj2.PERSONID as tj2_ID, tj2.BANK, tj2.TAX
FROM TEST_JOINED_1 tj1
LEFT JOIN TEST_JOINED_2 tj2 ON tj1.PERSONID = tj2.PERSONID
WHERE tj1.LASTNAME = 'LN_1';
Rewrite in FUNCTION:
------------------Rewritten in functions with ------------------
------------------Contraint 1: can only use RECORD instead of creating customized TYPE;------------
------------------Contraint 2: have to keep the content of the two subqueries under WITH clause exactly as it is --------------
CREATE OR REPLACE PACKAGE MY_JOIN_TEST_SP_PACKAGE_3 AS
TYPE join_record_type IS RECORD(
PersonID1 int,
LastName varchar2(255),
Address varchar2(255),
PersonID2 int,
Bank varchar2(20),
Tax int
);
TYPE join_record_table_type IS TABLE OF join_record_type;
FUNCTION get_joined_data(last_name VARCHAR2)
RETURN join_record_table_type
PIPELINED;
END;
/
CREATE OR REPLACE PACKAGE BODY MY_JOIN_TEST_SP_PACKAGE_3 AS
FUNCTION get_joined_data(last_name VARCHAR2)
RETURN join_record_table_type
PIPELINED
AS
join_record join_record_type;
BEGIN
FOR x IN (
-------------------start - WITH clause -- does this run for every RECORD x in the loop??? -----------------------
WITH
TEST_JOINED_1 AS (
SELECT
tps.PERSONID,
tps.LASTNAME,
tsd.ADDRESS
FROM TEST_PERSONS tps
LEFT JOIN TEST_ADDRESS_A tsd ON tps.personid = tsd.personid WHERE tps.LASTNAME = last_name
),
TEST_JOINED_2 AS (
SELECT
tps.PERSONID,
tsl.BANK,
tsl.TAX
FROM TEST_PERSONS tps
LEFT JOIN TEST_SALARY_A tsl ON tps.personid = tsl.personid WHERE tps.LASTNAME = last_name
)
-------------------end - WITH clause -------------------
-------------------start - main select-----------------------
SELECT tj1.PERSONID as tj1_ID, tj1.LASTNAME, tj1.ADDRESS, tj2.PERSONID as tj2_ID, tj2.BANK, tj2.TAX
FROM TEST_JOINED_1 tj1
LEFT JOIN TEST_JOINED_2 tj2 ON tj1.PERSONID = tj2.PERSONID
WHERE tj1.LASTNAME = last_name
-------------------end - main select--------------------------
)
LOOP
SELECT x.tj1_ID, x.LASTNAME, x.ADDRESS, x.tj2_ID, x.BANK, x.TAX
INTO join_record
FROM DUAL;
PIPE ROW (join_record);
END LOOP;
END;
END; -- END of CREATE
/
select * from table(MY_JOIN_TEST_SP_PACKAGE_3.get_joined_data('LN_1'));
EDITED: modified example code to have variable in the WITH clause
----------------------Create GLOBAL TEMPORARY TABLE -------------------------
DROP TABLE my_global_temp_table;
CREATE GLOBAL TEMPORARY TABLE my_global_temp_table (
PersonID int,
LastName varchar2(255),
Address varchar2(255),
Bank varchar2(20)
)
ON COMMIT DELETE ROWS;
----------------------Create PACKAGE AND FUNCTION -------------------------
CREATE OR REPLACE PACKAGE MY_JOIN_TEST_SP_PACKAGE_3 AS
TYPE join_record_type IS RECORD(
PersonID int,
LastName varchar2(255),
Address varchar2(255),
Bank varchar2(20)
);
TYPE join_record_table_type IS TABLE OF join_record_type;
FUNCTION get_joined_data(last_name VARCHAR2)
RETURN join_record_table_type
PIPELINED;
END;
/
CREATE OR REPLACE PACKAGE BODY MY_JOIN_TEST_SP_PACKAGE_3 AS
FUNCTION get_joined_data(last_name VARCHAR2)
RETURN join_record_table_type
PIPELINED
AS
join_record join_record_type;
BEGIN
--------------------use GLOBAL TEMPORARY TABLE-------------------------
INSERT INTO my_global_temp_table
-------------------start - WITH ... SELECT ... clause -- does this run for every RECORD x in the loop??? -----------------------
WITH
TEST_JOINED_1 AS (
SELECT
tps.PERSONID,
tps.LASTNAME,
tsd.ADDRESS
FROM TEST_PERSONS tps
LEFT JOIN TEST_ADDRESS_A tsd ON tps.personid = tsd.personid
WHERE tps.LASTNAME = last_name
)
SELECT tj1.PERSONID, tj1.LASTNAME, tj1.ADDRESS, ts.BANK
FROM TEST_JOINED_1 tj1
LEFT JOIN TEST_SALARY_A ts ON tj1.PERSONID = ts.PERSONID
WHERE tj1.LASTNAME = last_name;
-------------------end - WITH ... SELECT ... clause --
FOR x IN (
SELECT * FROM my_global_temp_table
)
LOOP
SELECT x.PERSONID, x.LASTNAME, x.ADDRESS, x.BANK
INTO join_record
FROM DUAL;
PIPE ROW (join_record);
END LOOP;
END;
END; -- END of CREATE
/
--------------------Call the FUNCTION-------------------------
select * from table(MY_JOIN_TEST_SP_PACKAGE_3.get_joined_data('LN_1'));
EDITED: following #Littefoot suggestion, try out using CREATE GLOBAL TEMP table but giving out '17/21 PL/SQL: ORA-00936: missing expression'. I am not sure why?
EDITED: Corrected the Insert syntax, but will get error 'ORA-14551: cannot perform a DML operation inside a query', I believe that this is because I called the function that contains that Insert from a SELECT
If you are not using variables and other pl sql constructs, I would suggest you break with clauses in the form of a table or materialize view. In this way you don't need to take risk of rewriting the logic of query in pl sql block and missing something.
I would suggest using materialize view advantage of materialize view over the table is that you don't need to drop table next time you load data and also you can use nologging with materialized view.No logging makes it very fast.
It will be very fast and have minimum risk.
Thanks
Bhanu Yadav
As there's nothing dynamic in the WITH factoring clause (i.e. you don't use variables - at least, I didn't notice any), I'd suggest you to create a view (based on that WITH) and use it whenever needed.
If the real query is really complex and takes time to execute, you could create a global temporary table (GTT), most probably choosing to keep its data during the session (ON COMMIT PRESERVE ROWS) properly index it and store view (or WITH's) contents in there. Then you'd use the GTT in your code
Although, Oracle will keep date returned by a query in memory so you might even have to really "execute" it once, but the memory isn't unlimited so ... test it, compare results you get, pick the one that seems to be the best.
To me, the GTT idea sounds promising, but without actual information it is difficult to decide.
[EDIT, about GTT]
Oracle's "Global temporary table" is, actually, "local" from your point if view (note that, if you're on 18c (I don't think you are, though) you can create a private temporary table). You create it once, using the create global temporary table .... Data you insert into it is visible ONLY to you, nobody else; it is restricted to your own transaction (if it is created with the ON COMMIT DELETE ROWS) or session (ON COMMIT PRESERVE ROWS). Pick the one that suits you best.
What does it mean? It means that you'd create the GTT once, providing column list and their datatypes. Every user, that uses your procedure, would insert its own data set into it (you'd use a query with the LAST NAME parameter, as you said) and use it throughout the transaction (or session). Many users can do that at the same time, but - as I've said - everyone would see only its own data.
Here's the pseudocode:
-- create table once. Do NOT create it, drop it, create again tomorrow, drop ...
-- Create it once, use it many times.
create global temporary table gtt_my_data
(id number,
c_name varchar2(20), ...
)
on commit preserve rows;
create index i1_gmd_id on gtt_my_data (id);
-- your procedure
procedure p_myproc (par_last_name in varchar2) is
begin
insert into gtt_my_data (id, c_name, ...)
select id, c_name, ...
from some_table join some_other_table ...
where some_table.last_name = par_last_name;
-- now, do whatever you do. When you need to fetch data from the GTT, do so
select ... into ...
from table_x join gtt_my_data on ...
update ... set some_column = (select another_column
from table_y join gtt_my_data on ...
)
end;
Once you're done: if you end the session, data will be removed from the GTT. If you want, you can do it manually (either delete or truncate its contents).
[EDIT #2: INSERT INTO A GTT]
Insert is wrong; you don't insert values, but something like this:
INSERT INTO my_global_temp_table
WITH test_joined_1 AS
(SELECT tps.personid,
tps.lastname,
tsd.address
FROM test_persons tps
LEFT JOIN test_address_a tsd ON tps.personid = tsd.personid
WHERE tps.lastname = last_name
)
SELECT tj1.personid,
tj1.lastname,
tj1.address,
ts.bank
FROM test_joined_1 tj1
LEFT JOIN test_salary_a ts ON tj1.personid = ts.personid
WHERE tj1.lastname = last_name;
Simplified, on Scott's schema:
SQL> create table test (empno number, deptno number);
Table created.
SQL> insert into test (empno, deptno)
2 with temp as
3 (select empno, deptno from emp)
4 select t.empno, t.deptno
5 from temp t join dept d on d.deptno = t.deptno
6 where d.deptno = 10;
3 rows created.
SQL>
Below you will find three sample tables and data along with a query. This example might seem contrived, but it is part of much larger (nearly 1500 lines) SQL query. The original query works great, but I've run into a problem while adding some new functionality.
CREATE TABLE rule_table (
id_rule_table NUMBER (10),
name VARCHAR2 (24),
goal NUMBER (10),
amount NUMBER (10)
);
INSERT INTO rule_table (id_rule_table, name, goal, amount) VALUES(1, 'lorem', 2, 3);
INSERT INTO rule_table (id_rule_table, name, goal, amount) VALUES(2, 'ipsum', 3, 3);
INSERT INTO rule_table (id_rule_table, name, goal, amount) VALUES(3, 'dolor', 4, 3);
CREATE TABLE content_table (
id_content_table NUMBER (10),
name VARCHAR2 (24),
show_flag NUMBER (10)
);
INSERT INTO content_table (id_content_table, name, show_flag) VALUES(1, 'lorem', 0);
INSERT INTO content_table (id_content_table, name, show_flag) VALUES(2, 'ipsum', 1);
INSERT INTO content_table (id_content_table, name, show_flag) VALUES(3, 'dolor', 1);
CREATE TABLE module_table (
id_module_table NUMBER (10),
id_content_table NUMBER (10),
name VARCHAR2 (24),
amount NUMBER (10)
);
INSERT INTO module_table (id_module_table, id_content_table, name, amount) VALUES(1, 2, 'lorem', 10);
INSERT INTO module_table (id_module_table, id_content_table, name, amount) VALUES(2, 2, 'ipsum', 11);
INSERT INTO module_table (id_module_table, id_content_table, name, amount) VALUES(3, 2, 'dolor', 12);
SELECT RULE.id_rule_table
FROM rule_table RULE
WHERE (
CASE
WHEN RULE.goal <= (
WITH contentTbl (id_content_table)
AS (
SELECT id_content_table
FROM content_table
WHERE show_flag = 1
),
modulesTbl (id_content_table, id_module_table)
AS (
SELECT C.id_content_table, M.id_module_table
FROM contentTbl C
JOIN module_table M ON M.id_content_table = C.id_content_table
WHERE 4 < M.amount - RULE.amount
)
SELECT SUM(M.id_module_table)
FROM contentTbl C
JOIN modulesTbl M ON C.id_content_table = M.id_content_table
)
THEN 1
ELSE 0
END
) = 1;
DROP TABLE rule_table;
DROP TABLE content_table;
DROP TABLE module_table;
If you try this you will receive the error ORA-00904: "RULE"."AMOUNT": invalid identifier. The problem lies with the line "WHERE 4 < M.amount - RULE.amount".
If you replace RULE.amount, in that line, with some number (e.g., WHERE 4 < M.amount - 3) then the query will run just fine.
As mentioned above, this is a snippet test case from a much larger query, so the structure of the query can't be (or hopefully doesn't need to be) changed too much. That is, ideally I'm looking for a solution that will allow me to use RULE.amount in the sub-query without changing anything other that the SQL inside of the "WHEN RULE.goal <= ()" block.
I'm trying to run this on Oracle 11g.
One last thing, I tried searching google and stackoverflow for solutions, but I couldn't figure out the correct terminology to describe my issue. The closest thing seemed to be nested correlated subquery, but that doesn't seem to be exactly right.
Taking into account that this is only part of a much larger query, here are the surgical changes required to make this work:
Move the WHERE 4 < M.amount - RULE.amount condition out of the CTE and into the main query so that RULE is in scope.
Modify the modulesTbl CTE to return an additional column amount so that M.amount is now available to the main query.
With these 2 changes, the query would look like this:
SELECT RULE.id_rule_table
FROM rule_table RULE
WHERE (
CASE
WHEN RULE.goal <= (
WITH contentTbl (id_content_table)
AS (
SELECT id_content_table
FROM content_table
WHERE show_flag = 1
),
modulesTbl (id_content_table, id_module_table, amount) -- add amount
AS (
SELECT C.id_content_table, M.id_module_table, M.amount -- add amount
FROM contentTbl C
JOIN module_table M ON M.id_content_table = C.id_content_table
)
SELECT SUM(M.id_module_table)
FROM contentTbl C
JOIN modulesTbl M ON C.id_content_table = M.id_content_table
AND 4 < M.amount - RULE.amount -- moved from CTE to here
)
THEN 1
ELSE 0
END
) = 1;
I am having trouble merging a table with a collection.
Let's say I have a table emp.
Here is my PL/SQL code snippet.
TYPE empcol is table of emp%ROWTYPE INDEX BY BINARY_INTEGER;
tmpemp empcol;
-- Code here to load data from a CSV file into tmpemp collection
-- tmpemp(1).emp_id := parsedstring
-- etc.
MERGE INTO emp A using tmpemp B ON A.emp_id = B.emp_id
WHEN MATCHED THEN UPDATE SET A.fname = B.fname, A.lname = B.lname
WHEN NOT MATCHED THEN INSERT (emp_id, fname, lname) VALUES (b.emp_id, b.fname, b.lname);
Compiler doesn't like it. Its throwing ORA-0942 - Table or View doesn't exist.
What am I doing wrong? or How can I do this better.
Thanks a lot for any help you can provide.
PL/SQL types like emp%ROWTYPE or TABLE OF ... INDEX BY ... cannot be used in SQL queries.
The type must be declared as SQL type (not as PL/SQL type) to be used in SQL query.
Try this approach (example):
create table emp(
firstname varchar2(100),
salary number
);
insert into emp values( 'John', 100 );
commit;
create type my_emp_obj is object(
firstname varchar2(100),
salary number
);
/
create type my_emp_obj_table is table of my_emp_obj;
/
declare
my_emp_tab my_emp_obj_table;
begin
null;
my_emp_tab := my_emp_obj_table( my_emp_obj( 'John', 200 ), my_emp_obj( 'Tom', 300 ));
MERGE INTO emp
USING ( SELECT * FROM TABLE( my_emp_tab )) src
ON ( emp.firstname = src.firstname )
WHEN MATCHED THEN UPDATE SET salary = src.salary
WHEN NOT MATCHED THEN INSERT VALUES( src.firstname, src.salary );
end;
/
select * from emp;
FIRSTNAME SALARY
----------------------- ----------
John 200
Tom 300
i have a library database which wrote a query to display count of loaned books by employee like this:
select Emploee.[Emp-No],count(*) as ecount
from Emploee
inner join Loan on Emploee.[Emp-No]=Loan.[Emp-No]
inner join Book on Loan.ISBN=Book.ISBN group by Emploee.[Emp-No]
the result of above query is something like this:
Emp-No ecount
------------------
1000 4
1001 2
1002 3
now i want to modify the output and make a comparison between ecount column of each row of result with another query which give me count of loaned books based on specific published by that user
in other word the result im looking for is something like this
Emp-No ecount
-----------------
1000 4
assume Employee 1000 loaned all of his book from one publisher. he will be showen in the result.
something like this
"..... my query...." having ecount=
(select count(*) from books where publisher='A')
but i cant use the result ecount in another query :(
After clarification, I understood the question as follows: return those employees which only loaned books from a single publisher.
You can do that by using COUNT(DISTINCT publisher) in your HAVING clause.
Like so:
declare #employee table (id int);
declare #books table (isbn int, title varchar(50), publisher varchar(50));
declare #loan table (employee_id int, book_isbn int);
insert #employee values (1000);
insert #employee values (1001);
insert #employee values (1002);
insert #books values (1, 'Some Book', 'Publisher A');
insert #books values (2, 'Some Book', 'Publisher A');
insert #books values (3, 'Some Book', 'Publisher A');
insert #books values (4, 'Some Book', 'Publisher B');
insert #books values (5, 'Some Book', 'Publisher B');
insert #books values (6, 'Some Book', 'Publisher B');
insert #loan values (1000, 1);
insert #loan values (1000, 2);
insert #loan values (1001, 3);
insert #loan values (1001, 4);
insert #loan values (1001, 5);
-- Show the number of different publishers per employee
select e.id, count(*) as ecount, count(DISTINCT b.publisher) as publishers
from #employee e
inner join #loan l on e.id = l.employee_id
inner join #books b on l.book_isbn = b.isbn
group by e.id
-- yields: id ecount publishers
-- ----------- ----------- -----------
-- 1000 2 1
-- 1001 3 2
-- Filter on the number of different publishers per employee
select e.id, count(*) as ecount
from #employee e
inner join #loan l on e.id = l.employee_id
inner join #books b on l.book_isbn = b.isbn
group by e.id
having count(DISTINCT b.publisher) = 1
-- yields: id ecount
-- ----------- -----------
-- 1000 2
To refer to an aggregate in another query, the aggregate has to have an alias. SQL Server does not allow you to refer to an alias at the same level. So you need a subquery to define the alias, before you can use the alias in another subquery.
For example, the following SQL uses a subquery to define alias bookcount for count(*). Thanks to this first subquery, the second subquery in the where clause can use the bookcount:
declare #books table (title varchar(50), author varchar(50));
declare #author_filter table (name varchar(50), bookcount int);
insert #books values
('The Lord of the Rings', 'J.R.R. Tolkien'),
('The Silmarillion', 'J.R.R. Tolkien'),
('A Song for Arbonne', 'G.G. Kay');
insert #author_filter values
('2_books', 2);
select *
from (
select author
, count(*) as bookcount
from #books
group by
author
) authors
where '2_books_filter' =
(
select filter.name
from #author_filter filter
where authors.bookcount = filter.bookcount
)