SQL Help Using NESTED SELECTION - sqlite

Here's my table
CREATE TABLE emp (num INTEGER NOT NULL,
name VARCHAR(20) NOT NULL,
dept VARCHAR(20) NOT NULL,
salary INTEGER NOT NULL,
boss INTEGER NOT NULL,
PRIMARY KEY (num),
FOREIGN KEY (boss) REFERENCES emp (num)
);
INSERT INTO emp
VALUES ('1', 'PRESIDENT', 'gh', '10000', '1');
INSERT INTO emp
VALUES ('2', 'Bob', 'Slave', '6456', '3');
INSERT INTO emp
VALUES ('3', 'Matthew', 'M', '1', '1');
INSERT INTO emp
VALUES ('4', 'Marl', 'P', '534465', '2');
INSERT INTO emp
VALUES ('5', 'Apple', 'P', '554545646512', '2');
INSERT INTO emp
VALUES ('6', 'Roy', 'Slave', '125', '1');
INSERT INTO emp
VALUES ('7', 'Marth', 'Slave', '56456', '1');
INSERT INTO emp
VALUES ('8', 'Mart', 'Slave', '98', '3');
Here are my Queries:
SELECT * FROM emp;
SELECT * FROM emp
WHERE boss = (SELECT num FROM emp
WHERE num = boss) AND num != boss;
SELECT e1.num,e1.name FROM emp e1
WHERE
(SELECT e2.salary FROM emp e2
WHERE e2.boss = (SELECT e3.num FROM emp e3
WHERE e3.num = e3.boss) AND e2.num != e2.boss) < 98;
So the first output prints out everything as expected. The second output prints out: Matthew, Roy and Marth as expected.
But the final output prints out one.
This is a practice test question I was given
My goal is print the num and name of everyone working under the president who makes less than MIN of people who aren't working for the president.
I can calculate the MIN with the following code:
SELECT min(salary) FROM emp
WHERE boss != (SELECT num FROM emp
WHERE num = boss);
Ideally I want to replace 98 in the previous query with this statement, but I decided it would be best if I broke it down and tried one thing at a time.

Your problem is too many subqueries.
e2 is completely independent from e1; the e2 subquery returns all salaries of those working under the president; the comparison is then against the first value returned.
You could use a correlated subquery to tie e2 to e1, but you don't need a subquery at all:
SELECT e1.num,
e1.name
FROM emp AS e1
WHERE e1.boss = (SELECT e3.num
FROM emp AS e3
WHERE e3.num = e3.boss)
AND e1.num != e1.boss
AND e1.salary < 98;

Related

How to get the last updated value of a value in sql

sample data I have 2 columns old_store_id, changed_new_store_id and there are cases when changed_new_store_id value will also get updated to new value. how can i traverse through DB(teradata) to get the last value (changed_new_store_id ) of the respective old_store_id
let say in 1 st row
old_store_id = A ;
changed_new_store_id = B
and 5 th row contains
old_store_id = B ;
changed_new_store_id = C
and some other nth row C is changed to X etc
how to get final value of A which is X ?
I can try using multiple self joins
using Stored procedure but it will not be an efficient way (for many reasons)
Is there any way to find ?
Please anyone suggest me
This assumes no "loops", and uses "bottom-up" recursion. Something very similar could be done "top-down", limiting the seed query to rows where the "old" value doesn't appear anywhere as a "new" value.
CREATE VOLATILE TABLE #Example (
Old_Store_ID VARCHAR(8),
New_Store_ID VARCHAR(8)
)
PRIMARY INDEX(Old_Store_ID)
ON COMMIT PRESERVE ROWS;
INSERT INTO #Example VALUES ('A', 'B');
INSERT INTO #Example VALUES ('D', 'c');
INSERT INTO #Example VALUES ('B', 'F');
INSERT INTO #Example VALUES ('c', 'FF');
INSERT INTO #Example VALUES ('FF', 'GG');
INSERT INTO #Example VALUES ('F', 'X');
WITH RECURSIVE #Traverse(Old_Store_ID,New_Store_ID,Final_ID)
AS
(
--Seed Query - start with only the rows having no further changes
SELECT Old_Store_ID
,New_Store_ID
,New_Store_ID as Final_ID
FROM #Example as This
WHERE NOT EXISTS (
SELECT 1 FROM #Example AS Other WHERE This.New_Store_ID = Other.Old_Store_ID
)
UNION ALL
--Recursive Join
SELECT NewRow.Old_Store_ID
,NewRow.New_Store_ID
,OldRow.Final_ID
FROM #Example AS NewRow
INNER JOIN #Traverse AS OldRow
ON NewRow.New_Store_ID = OldRow.Old_Store_ID
)
SELECT *
FROM #Traverse
;
A recursive answer:
CREATE VOLATILE TABLE #SearchList (
SearchID CHAR(2),
ParentSearchID CHAR(2)
)
PRIMARY INDEX(SearchID)
ON COMMIT PRESERVE ROWS;
INSERT INTO #SearchList VALUES ('A', 'B');
INSERT INTO #SearchList VALUES ('D', 'c');
INSERT INTO #SearchList VALUES ('B', 'F');
INSERT INTO #SearchList VALUES ('c', 'FF');
INSERT INTO #SearchList VALUES ('FF', 'GG');
INSERT INTO #SearchList VALUES ('F', 'X');
CREATE VOLATILE TABLE #IntermediateResults(
SearchID CHAR(2),
ParentSearchID CHAR(2),
SearchLevel INTEGER
)
ON COMMIT PRESERVE ROWS;
INSERT INTO #IntermediateResults
WITH RECURSIVE RecursiveParent(SearchID,ParentSearchID,SearchLevel)
AS
(
--Seed Query
SELECT SearchID
,ParentSearchID
,1
FROM #SearchList
UNION ALL
--Recursive Join
SELECT a.SearchID
,b.ParentSearchID
,SearchLevel+1
FROM #SearchList a
INNER JOIN RecursiveParent b
ON a.ParentSearchID = b.SearchID
)
SELECT SearchID
,ParentSearchID
,MAX(SearchLevel)
FROM RecursiveParent
GROUP BY SearchID
,ParentSearchID
;
SELECT RESULTS.*
FROM #IntermediateResults RESULTS
INNER JOIN (SELECT RESULTS_MAX.SearchID
,MAX(RESULTS_MAX.SearchLevel) MaxSearchLevel
FROM #IntermediateResults RESULTS_MAX
GROUP BY RESULTS_MAX.SearchID
) GROUPED_RESULTS
ON RESULTS.SearchID = GROUPED_RESULTS.SearchID
AND RESULTS.SearchLevel = GROUPED_RESULTS.MaxSearchLevel
ORDER BY RESULTS.SearchID ASC
,RESULTS.SearchLevel ASC
;
Output:
SearchID ParentSearchID SearchLevel
-------- -------------- -----------
A X 3
B X 2
c GG 2
D GG 3
F X 1
FF GG 1

Oracle Query on 3 tables with 2 outer joins

I'm having some trouble writing a query that seems like it should be simple, but the solution is evading me.
We have three tables (simplified for the purpose of this question):
persons - a table of user names:
per_id number(10) - primary key, populated by a sequence
user_name varchar2(50)
user_id varchar2(15) - unique, basically the employee ID
work_assignments - kind of like crew assignments, but more general:
wa_id number(10) - primary key, populated by a sequence
wa_name varchar2(25)
current_assignments - which users have which work_assignments; the average per user is about 25 work assignments, but some "lucky" individuals have upwards of 150:
wa_id number(10)
per_id number(10)
I'm trying to write a query that will compare the work_assignments for two users, in a total of three columns. The results should look like this:
WA_Name User_Name1 User_Name2
Crew A Bob Joe
Crew B Joe
Crew C Bob
Basically, every work_assignment that either of the two user has, with the name(s) of the user(s) who has it.
Here's the closest I could come up with (well, I did come up with an ugly query with 3 subqueries that does the job, but it seems like there should be a more elegant solution):
select distinct * from (
select wa.name work_assignment,
per.name user_name1,
per2.name user_name2
from work_assignments wa join current_assignments ca on wa.wa_id = ca.wa_id
join current_assignments ca2 on wa.wa_id = ca2.wa_id
left outer join persons per on per.per_id = ca.per_id and per.user_id = 'X12345'
left outer join persons per2 on per2.per_id = ca2.per_id and per2.user_id = 'Y67890'
)
where user_name1 is not null or user_name2 is not null
order by 1;
The problem with this one is that if both users have a work assignment, it shows 3 records: one for Bob, one for Joe, and one for both:
WA_Name User_Name1 User_Name2
Crew A Bob Joe
Crew A Joe
Crew A Bob
Please help!
Thanks,
Dan
I created a set of sample data/tables
drop table persons;
drop table work_assgn;
drop table curr_assgn;
create table persons(
per_id number(10) not null
, user_name varchar2(10) not null
, user_id varchar2(10) not null
)
;
insert into persons values( 1, 'Bob', 'X123' );
insert into persons values( 2, 'Joe', 'Y456' );
insert into persons values( 3, 'Mike', 'Z789' );
insert into persons values( 4, 'Jeff', 'J987' );
commit;
create table work_assgn(
wa_id number(10) not null
, wa_name varchar2(25)
)
;
insert into work_assgn values( 10, 'Crew A' );
insert into work_assgn values( 20, 'Crew B' );
insert into work_assgn values( 30, 'Crew C' );
insert into work_assgn values( 40, 'Crew D' );
commit;
create table curr_assgn(
wa_id number(10) not null
, per_id number(10) not null
)
;
insert into curr_assgn values( 10, 1 );
insert into curr_assgn values( 10, 2 );
insert into curr_assgn values( 20, 2 );
insert into curr_assgn values( 30, 1 );
insert into curr_assgn values( 40, 4 );
commit;
select * from persons;
select * from work_assgn;
select * from curr_assgn;
So the data looks like
PERSONS
PER_ID USER_NAME USER_ID
---------- ---------- ----------
1 Bob X123
2 Joe Y456
3 Mike Z789
4 Jeff J987
WORK_ASSGN
WA_ID WA_NAME
---------- -------------------------
10 Crew A
20 Crew B
30 Crew C
40 Crew D
CURRASSGN
WA_ID PER_ID
---------- ----------
10 1
10 2
20 2
30 1
40 4
One approach may be to use a PIVOT
with assignment as
(
select p.user_id, p.user_name, a.wa_name
from persons p
join curr_assgn c
on p.per_id =c.per_id
join work_assgn a
on a.wa_id = c.wa_id
where p.user_id in ( 'X123', 'Y456' )
)
select * from assignment
pivot
( max(user_name) for user_id in ( 'X123', 'Y456' )
)
;

SQLite FTS4 with preferred language

I have an SQLite table that was generated by using the FTS4 module. Each entry is listed at least twice with different languages, but still sharing a unique ID (int column, not indexed).
Here is what I want to do:
I want to lookup a term in a preferred language. I want to union the result with a lookup for the same term using another language.
For the second lookup though, I want to ignore all entries (identified by their ID) that I already found during the first lookup. So basically I want to do this:
WITH term_search1 AS (
SELECT *
FROM myFts
WHERE myFts MATCH 'term'
AND languageId = 1)
SELECT *
FROM term_search1
UNION
SELECT *
FROM myFts
WHERE myFts MATCH 'term'
AND languageId = 2
AND id NOT IN (SELECT id FROM term_search1)
The problem here is, that the term_seach1 Query would be executed twice. Is there a way of materializing my results maybe? Any solution for limiting it to 2 Queries (instead of 3) would be great.
I also tried using recursive Queries, something like:
WITH RECURSIVE term_search1 AS (
SELECT *
FROM myFts
WHERE myFts MATCH 'term'
AND languageId = 1
UNION ALL
SELECT m.*
FROM myFts m LEFT OUTER JOIN term_search1 t ON (m.id = t.id)
WHERE myFts MATCH 'term'
AND m.languageId = 2
AND t.id IS NULL
)
SELECT * FROM term_search1
This didn't work neither. Apparently he just executed two lookups for languageId = 2 (is this a bug maybe?).
Thanks in advance :)
You can use TEMPORARY tables to reduce the number of queries to myFts to 2:
CREATE TEMP TABLE results (id INTEGER PRIMARY KEY);
INSERT INTO results
SELECT id FROM myFts
WHERE myFts MATCH 'term' AND languageId = 1;
INSERT INTO results
SELECT id FROM myFts
WHERE myFts MATCH 'term' AND languageId = 2
AND id NOT IN (SELECT id FROM results);
SELECT * FROM myFts
WHERE id IN (SELECT id FROM results);
DROP TABLE results;
If it's possible to change the schema, you should only keep text data in the FTS table. This way you will avoid incorrect results when you are searching for numbers and rows matching languageId is not desired. Create another meta table holding non-textual data (like id and languageId) and filter the rows by joining against the rowid of the myFts. This way you will need to query the FTS table only once - use the temporary table to store the FTS table results then use the meta table to order them.
This is the best I can think of :
SELECT *
FROM myFts t1
JOIN (SELECT COUNT(*) AS cnt, id
FROM myFts t2
WHERE t2.languageId in (1, 2)
AND t2.myFts MATCH 'term'
GROUP BY t2.id) t3
ON t1.id = t3.id
WHERE t1.myFts MATCH 'term'
AND t1.languageId in (1, 2)
AND (t1.languageId = 1 or t3.cnt = 1)
I am not sure if the second MATCH clause is necessary.
The idea is to first count the acceptable rows, then choose the best one.
Edit : I have no idea why it does not work with your table. This is what I did to test it (SQLite version 3.8.10.2):
CREATE VIRTUAL TABLE myFts USING fts4(
id integer,
languageId integer,
content TEXT
);
insert into myFts(id, languageId, content) values (10, 1, 'term 10 lang 1');
insert into myFts(id, languageId, content) values (10, 2, 'term 10 lang 2');
insert into myFts(id, languageId, content) values (11, 1, 'term 11 lang 1');
insert into myFts(id, languageId, content) values (12, 2, 'term 12 lang 2');
insert into myFts(id, languageId, content) values (13, 1, 'not_erm 13 lang 1');
insert into myFts(id, languageId, content) values (13, 2, 'term 13 lang 2');
executing the query gives :
sqlite> SELECT *
...> FROM myFts t1
...> JOIN (SELECT COUNT(*) AS cnt, id
...> FROM myFts t2
...> WHERE t2.languageId in (1, 2)
...> AND t2.myFts MATCH 'term'
...> GROUP BY t2.id) t3
...> ON t1.id = t3.id
...> WHERE t1.myFts MATCH 'term'
...> AND t1.languageId in (1, 2)
...> AND (t1.languageId = 1 or t3.cnt = 1);
10|1|term 10 lang 1|2|10
11|1|term 11 lang 1|1|11
12|2|term 12 lang 2|1|12
13|2|term 13 lang 2|1|13
sqlite>

Delete data using merge statement in Oracle

How to only delete data using merge using oracle...
I am using the below code:
Merge
into
target_table
using
source_tablle
on (...)
when matched
then delete
But I am getting an error "missing Keyword" at last line
Your MERGE at the end is missing the UPDATE clause.
Lets look at a sample MERGE
CREATE TABLE employee (
employee_id NUMBER(5),
first_name VARCHAR2(20),
last_name VARCHAR2(20),
dept_no NUMBER(2),
salary NUMBER(10));
INSERT INTO employee VALUES (1, 'Dan', 'Morgan', 10, 100000);
INSERT INTO employee VALUES (2, 'Helen', 'Lofstrom', 20, 100000);
INSERT INTO employee VALUES (3, 'Akiko', 'Toyota', 20, 50000);
INSERT INTO employee VALUES (4, 'Jackie', 'Stough', 20, 40000);
INSERT INTO employee VALUES (5, 'Richard', 'Foote', 20, 70000);
INSERT INTO employee VALUES (6, 'Joe', 'Johnson', 20, 30000);
INSERT INTO employee VALUES (7, 'Clark', 'Urling', 20, 90000);
CREATE TABLE bonuses (
employee_id NUMBER, bonus NUMBER DEFAULT 100);
INSERT INTO bonuses (employee_id) VALUES (1);
INSERT INTO bonuses (employee_id) VALUES (2);
INSERT INTO bonuses (employee_id) VALUES (4);
INSERT INTO bonuses (employee_id) VALUES (6);
INSERT INTO bonuses (employee_id) VALUES (7);
COMMIT;
Now we have a sample data structure lets do some merging:
MERGE INTO bonuses b
USING (
SELECT employee_id, salary, dept_no
FROM employee
WHERE dept_no =20) e
ON (b.employee_id = e.employee_id)
WHEN MATCHED THEN
UPDATE SET b.bonus = e.salary * 0.1
DELETE WHERE (e.salary < 40000)
;
So this command the MERGE syntax using the merge_update_clause:
MERGE INTO (table/view)
USING (table/view)
ON (condition)
WHEN MATCHED THEN
UPDATE SET (column..expression)
DELETE WHERE (condition)
I guess what I'm hinting at is that you are missing your UPDATE SET clause as well as the DELETE conditions. I recommend following up on the MERGE syntax.
**Edit:**SQLFiddle is back so here you go.

T-Sql help needed in Sql Server 2005

I have created one stored procedure which runs on 5000 users in tbluser table with some filter condition in database.There are 4 filtering condition(FC1,FC2,FC3,FC4).Filtering condition has some ListBox and dropdown list of department and countries.I want output as given below:
ID Name StaffNo department Points
1 KK 111 dep1 2
2 NN 222 dep2 1
3 DD 333 dep3 4
I got ID,Name,StaffNo,department in resultset but not points.
points calculation would be based on filtering condition like
if FC1 matched user gained point 1,if both FC1 and FC2 matched user gained 2 point,if both FC1 ,FC2 and FC3 matched user gained 3 point etc.
--in stored procedure i m using dynamic query
DECLARE #SQL VARCHAR(2000)
SET #SQL = 'SELECT U.UserID, U.StaffNo,U.FirstName+'' ''+ U.LastName AS EmployeeName,''?'' AS Points FROM tblUser U '
SET #SQL = #SQL+' WHERE U.Department in (' + #SqlDepartment + ') '
---------------------Update---------------------------------------
IF #SqlLanguage <> ''
SET #SQL = #SQL+' OR U.UserID IN (SELECT UserID FROM Country WHERE LCValues IN ('+ #SqlLanguage +') )'
IF #SqlAreas <> ''
SET #SQL = #SQL+' OR U.UserID IN (SELECT UserID FROM tblAreas WHERE '+#SqlAreas+')'
---------------------Update---------------------------------------
...other filtering condition
EXEC (#SQL)
all filtering condition are implemented with OR logic.
Have you tried implementing a CASE statement to calculate the "Points"?
--in stored procedure i m using dynamic query
DECLARE #SQL VARCHAR(2000)
SET #SQL = '
SELECT
U.[UserID],
U.[StaffNo],
U.[FirstName]+'' ''+ U.[LastName] AS EmployeeName,
(
CASE WHEN EXISTS(SELECT 1 FROM [Country] WHERE /*Your filter comes in here*/) THEN 1 ELSE 0 END +
CASE WHEN EXISTS(SELECT 1 FROM [tblAreas] WHERE /*Your filter comes in here*/) THEN 1 ELSE 0 END +
CASE WHEN EXISTS(SELECT 1 FROM [OtherTable1] WHERE /*Your filter comes in here*/) THEN 1 ELSE 0 END +
CASE WHEN EXISTS(SELECT 1 FROM [OtherTable2] WHERE /*Your filter comes in here*/) THEN 1 ELSE 0 END
) AS Points
FROM [tblUser] U'
SET #SQL = #SQL+' WHERE U.Department in (' + #SqlDepartment + ') OR'...
...other filtering condition
EXEC (#SQL)
I think you might be better off working out the points in your code rather than in the SQL
The only way I can think of doing it is with UNIONS which would be horrible
You can use left outer join to your filter condition, group by UserID to not get duplicates, add a value of 1 for hits and use coalesce to set 0 for no hits.
Some sample code that shows what I mean using Area and Country as filter condition.
declare #U table (UserID int, Name varchar(50), StaffNo char(3), department char(4))
declare #C table (CountryID int, UserID int)
declare #A table (AreaID int, UserID int)
insert into #U values (1, 'KK', '111', 'dep1')
insert into #U values (2, 'NN', '222', 'dep2')
insert into #U values (3, 'DD', '333', 'dep3')
insert into #C values(1, 1)
insert into #C values(2, 1)
insert into #C values(3, 2)
insert into #C values(3, 3)
insert into #A values(1, 1)
insert into #A values(2, 1)
insert into #A values(3, 2)
select
U.UserID,
U.Name,
U.StaffNo,
U.department,
coalesce(C.Point, 0)+coalesce(A.Point,0) as Points
from #U as U
left outer join
(select UserID, 1 as Point
from #C
-- where ...?
group by UserID) as C
on U.UserID = C.UserID
left outer join
(select UserID, 1 as Point
from #A
-- where ...?
group by UserID) as A
on U.UserID = A.UserID

Resources