Newbie needs to multiple select of the same table - sql

I have a single table with people's info such as
ID
Family (last name)
Name (first name)
Status
IDFather
IDMother
I'd like to bring up the information containing childs' Name and Family, as well as their parent's ID, Name and Family, but only those records where parents are dead. So far I've done this, but it only selects children who are dead, not parents (status 5 is deceased).
select * from A1 where (IDFather IS NOT NULL and IDMother IS NOT NULL) and [Status] = '5'
Table sample below:
ID Family Name Status IDFather IDMother
001 Watson Jason 1 123 321
002 Smith Matt 5 333 111
003 Smith Mike 1 002 NULL
004 Derulo Sam 5 NULL NULL
005 Pitt Jenny 1 NULL 004
I tried this statement:
select * from Table1 where [Status] = '5'
I then wrote a select statement in C#
select ID,Name,Family from Table1 where IDFather = "value" or IDMother = "value"
At the end of the day I need the Name, Family of the child and Name and Family of their parents with parents ID.

select father.id,
father.family,
father.name
mother.id,
mother.family,
mother.name
child.id,
child.family,
child.name
from A1 father,
A1 mother,
A1 child
where child.IDFather = father.id
and child.IDMother = child.id
and (father.status = 5 OR mother.status = 5)
Replace the OR with AND if you want to retrieve records where both parents are dead
Oh, and if this is a homework assignment, you may want to rewrite my archaic join syntax with the more modern vernacular. I'm stuck in my ways...

Try this:
SELECT * from your_table
WHERE
IDFather IN
(SELECT ID from your_table WHERE Status = 5)
AND IDMother IN
(SELECT ID from your_table WHERE Status = 5)
If you want also dead people you could try:
SELECT * from your_table
WHERE
Status = 5
OR (
IDFather IN
(SELECT ID from your_table WHERE Status = 5)
AND IDMother IN
(SELECT ID from your_table WHERE Status = 5))

Related

SQL select units with condition of another column

I have this
ID
Product
001
A
001
B
001
C
002
A
002
A
002
D
003
G
003
D
003
C
004
G
004
D
004
R
and I wand ID list if they don't have product C...so:
002
You can apply the set difference between all ids and all ids with a "c" with the standard way of doing it: using the NOT IN operator in the WHERE clause.
SELECT DISTINCT ID
FROM tab
WHERE id NOT IN (SELECT ID FROM tab WHERE Product = 'C')
Consider below query:
SELECT DISTINCT ID FROM (
SELECT *, COUNTIF(product = 'C') OVER (PARTITION BY ID) AS cnt_C
FROM sample_table
) WHERE cnt_C = 0
output:
There are multiple ways of doing this, but I think a very readable way is to use NOT EXISTS.
SELECT DISTINCT id
FROM mytable t1
WHERE NOT EXISTS (SELECT 1 FROM mytable t2 WHERE t2.id = t1.id AND t2.Product = 'C')
The WHERE clause checks that there is no row with product C and the same id. The DISTINCT ensures you don't get multiples of the same id returned.
I am not sure but I think you just need a normal where
SELECT * FROM table_name WHERE product != product_name
*insert the table name in the place of Table_name and product name in the place of product_name
select distinct
id
from
your_table_name
where
product <> 'C';
This will return you list of ID's that don't have product C

Oracle SQL: Retrieving a record more than once

I'm using Oracle 11 and would like to be able to retrieve a record more than one in a query, which would be a good convenience saving for the next part of my code.
Let's consider this SQL statement:
SELECT ID, NAME FROM PEOPLE WHERE NAME IN ('Alice', 'Bob', 'Alice');
It returns this data:
| 1 | Alice |
| 2 | Bob |
What I'd really like to do is to un-uniquify that list and return the records with duplicates, in the order given. So the above statement would be:
| 1 | Alice |
| 2 | Bob |
| 1 | Alice |
I appreciate that Oracle is optimized to remove repetition like this, and I could re-use the data afterwards, keep it in a store object and retrieve by name etc. I was just wondering if there was a way to make this happen on the database itself.
Oracle has a couple of handy built-in functions that return lists of arguments that you can then transform to a table and join on it. In your case, odcivarchar2list can be used to return a list of varchar2s:
SELECT p.*
FROM TABLE(sys.odcivarchar2list('Alice', 'Bob', 'Alice')) dups
JOIN people p ON p.name = dups.column_value*
query below for record with duplicate
select x.id,x.name from (
select a.id,a.name from people a where a.name in ('Alice')
union all
select a.id,a.name from people a where a.name in ('Bob')
union all
select a.id,a.name from people a where a.name in ('Alice')
) x
Late to the party but just wanted to add you can use a traditional table expression:
select p.id, p.name
from (
select 'Alice' as name from dual
union all select 'Bob' from dual
union all select 'Alice' from dual
) searched s
join people p on p.name = s.name;
Here's another idea:
WITH cteNumbers as (SELECT LEVEL AS N
FROM DUAL
CONNECT BY LEVEL <= 2),
PEOPLE AS (SELECT 'Bob' AS NAME, 111 AS EMPID FROM DUAL UNION ALL
SELECT 'Carol' AS NAME, 222 AS EMPID FROM DUAL UNION ALL
SELECT 'Ted' AS NAME, 333 AS EMPID FROM DUAL UNION ALL
SELECT 'Alice' AS NAME, 444 AS EMPID FROM DUAL)
SELECT *
FROM PEOPLE p
CROSS JOIN cteNumbers
WHERE 1 = CASE
WHEN NAME = 'Alice' THEN 1
WHEN NAME = 'Bob' AND N = 1 THEN 1
WHEN NAME = 'Ted' AND N < 4 THEN 1
WHEN NAME = 'Carol' AND N = 3 THEN 1
ELSE 0
END
ORDER BY NAME, N
Basically, use cteNumbers to generate a list of number (in this case, from 1 to 2 - adjust the CONNECT BY LEVEL condition to control how many numbers are generated), then use the CASE expression in the WHERE clause to control the circumstances under which a particular record's repetitions are selected.
SQLFiddle here

SQL WHERE clause same column multiple times?

My table looks like this, let's call it Table1:
ID | value | formID
----------------------
25 Business 1001
16 John 1001
5 2/20/17 1001
30 FormXYZ 1001
25 Nursing 2345
16 Sam 2345
5 1/15/17 2345
30 FormXYZ 2345
25 Tech 4500
16 Sam 4500
5 2/1/17 4500
30 FormC 4500
The ID is the unique identifier of that field:
25 = Department
16 = Name
5 = Date
30 = Form Name, we have multiple different Forms, and I just need FormXYZ data.
FormID is a unique ID for each form submitted, the form contains 3 fields.
I have been trying to write a single query that retrieves all data looking something like this if possible:
Department | Name | Date
Business John 2/20/17
Nursing Sam 1/15/17
Here is what I have been trying, nesting and CASE didn't seem to work right for me, so I am posting here and I am right back where I started at.
SELECT value
FROM Table1
WHERE ID = '25'
UNION ALL
SELECT value
FROM Table1
WHERE ID = '16'
UNION ALL
SELECT value
FROM Table1
WHERE ID = '5'
UNION ALL
SELECT value
FROM Table1
WHERE ID = '30' and value = 'FormXYZ'
One way to transpose data in SQL is to use case statements and roll up the data using a group by:
select
formID,
max(case when ID=25 then value else null end) as Department,
max(case when ID=16 then value else null end) as Name,
max(case when ID=5 then value else null end) as Date
from Table1
group by formID
This produces:
formid Department Name Date
1001 Business John 2/20/17
2345 Nursing Sam 1/15/17
4500 Tech Sam 2/1/17
You can add a where clause as needed. This should get the data in a single scan.
select dp.value as department,
n.value as name,
dt.value as date
from
(select formID, value from table1 where id = 25) as dp
inner join (select formID, value from table1 where id = 16) as n
on dp.formID = n.formID
inner join (select formID, value from table1 where id = 5) as dt
on dp.formID = dt.formID
inner join (select formID, value from table1 where id = 30) as f
on dp.formID = f.formID
where f.value = 'FormXYZ';
OR
select
case when id = 25 then value end as department,
case when id = 5 then value end as date,
case when id = 16 then value end as name
from table1
where formId in (select formID from table1
where id = 30 and value = 'FormXYZ')
and id in (5,16,25);

Very challenging SQL interview (can't use stored procedure)

You have a SQL table table with two columns: name and pen. Both columns are text strings.
name | pen
---------------
mike | red
mike | red
mike | blue
mike | green
steve | red
steve | yellow
anton | red
anton | blue
anton | green
anton | black
alex | black
alex | green
alex | yellow
alex | red
Person's name is given as the input argument.
Please write a SQL statement (not a stored procedure) which returns names of persons having unique set of pens which is equivalent or wider/bigger than the set of pens of given person.
Examples:
input: mike
output: anton
Mike has (red, blue, green).
Anton has more gadgets (red, blue, green) + black.
input: steve
output: alex
Steve has (red, yellow).
Alex has (red, yellow) + green+ black.
Mike, Anton aren't printed - they do not have yellow.
input: alex
output:
Here's one way (Online Demo), assuming input name "steve".
This can be rephrased as "Looking for all users for which there does not exist a pen owned by steve that they do not own"
SELECT DISTINCT name
FROM table t1
WHERE NOT EXISTS (SELECT *
FROM table t2
WHERE name = 'steve'
AND NOT EXISTS (SELECT *
FROM table t3
WHERE t2.pen = t3.pen
AND t1.name = t3.name))
AND t1.name <> 'steve' /*Exclude input name from results*/
See this article for other techniques
with test1 as
(select a.name nm, count(distinct a.pen) ct
from table a, table b
where b.pen = a.pen
and b.name = 'anton'
group by a.name
order by 2 desc),
test2 as
(select nm name1
from test1
where ct = (select max(ct) from test1))
select distinct c.name
from table c
where c.name in (select name1
from test2
where name1 not in (case
when (select count(distinct name1) from test2) > 1 then
'anton'
else
' '
end))
Table Format:
E_NAME E_PEN
----------------
mike green
mike blue
mike red
mike red
steve red
steve yellow
anton red
anton blue
anton green
anton black
alex black
alex green
alex yellow
alex red
Query:
SELECT A.E_NAME, A.E_PEN
FROM V_NAME A, V_NAME B
WHERE TRIM(UPPER(A.E_NAME)) = TRIM(UPPER(B.E_NAME))
AND TRIM(UPPER(A.E_NAME)) != 'MIKE'
AND TRIM(UPPER(A.E_PEN)) = TRIM(UPPER(B.E_PEN(+)))
Procedure to take Input from user:
CREATE OR REPLACE PROCEDURE E_TEST(I_NAME VARCHAR2) AS
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE E_TABLE AS
SELECT A.E_NAME,A.E_PEN FROM V_NAME A,V_NAME B
WHERE TRIM(UPPER(A.E_NAME)) = TRIM(UPPER(B.E_NAME))
AND TRIM(UPPER(A.E_NAME)) != ''' ||
I_NAME || '''
AND TRIM(UPPER(A.E_PEN))= TRIM(UPPER(B.E_PEN(+)))';
END;
Name: Nikhil Shinde
E-Mail: nikhilshinde3jun#gmail.com
declare #t table (name nvarchar(20), pen nvarchar(20))
insert into #t (name,pen)
values('mike','red'),
('mike','red'),
('mike','blue'),
('mike','green'),
('steve','red'),
('steve','yellow'),
('anton','red'),
('anton','blue'),
('anton','green'),
('anton','black'),
('alex','black'),
('alex','green'),
('alex','yellow'),
('alex','red')
declare #input nvarchar(20) = 'mike';
with cte_input (name, pen)
as
(
select distinct name, pen
from #t
where name = #input
)
, cte_colors (name, matches)
as
(
select name, matches = count(distinct pen)
from #t
where name != #input
and pen in (select pen from cte_input)
group by name
having count(distinct pen) = (select count(1) from cte_input)
)
select t.name
from #t t
join cte_colors m on m.name = t.name
group by t.name, m.matches
having count(distinct t.pen) > m.matches
We can use LEAD analytical function to achieve this solution.
The query would be
select next_name from
(select name, count(pen) CNT, LEAD(name,1) over (order by count(pen)) next_name
from table
group by name order by CNT
)
where name=input_value;
the sub query will give the results as below
name | CNT | next_name
mike | 3 | Steve
steve | 2 | anton
anton | 4 | alex
alex | 4 | (null)
Then the out query filter the row required and gives the next_name which is what we are looking for.
create table practise
(
name varchar (10),pens varchar (10)
)
insert practise (name,pens)
select 'mike','red' union
select 'mike','red' union
select 'mike','blue' union
select 'mike','green' union
select 'steve','red' union
select 'steve','yellow' union
select 'anton','blue' union
select 'anton','green' union
select 'anton','black' union
select 'anton','red' union
select 'alex','black' union
select 'alex','green' union
select 'alex','yellow' union
select 'alex','red'
select * into #t from practise
update #t
set Colourcode = 1 where pens = 'black'
go
update #t
set Colourcode = 2 where pens = 'blue'
go
update #t
set Colourcode = 3 where pens = 'green'
go
update #t
set Colourcode = 4 where pens = 'red'
go
update #t
set Colourcode = 5 where pens = 'yellow'
select distinct colourcode from #t
/* IMP Code /
update #t
set Number =
(
select count() from #t
where name = 'steve' and colourcode = 1
)
where name = 'steve' and colourcode = 1
/* once the above update statement is exected change colourcode = 2 and again run the statement
change colourcode = 3 and again run the statement
change colourcode = 4 and again run the statement
change colourcode = 5 and again run the statement*/
/* Follow the same process for name 'alex','anton' and 'mike' */
/* Below is the Solution*/
select name from #t
where number =
(
select distinct number from #t
where colourcode = 3 and Number =1)
Declare #name nvarchar(20)
set #name = 'steve';
with cte_in
as
(
select distinct Pen from tblNamePen where Name = #name
)
select Name from
(
select t1.name, count(distinct t1.Pen) ct from tblNamePen t1
join cte_in inp on t1.Name <> #name
where t1.pen = inp.Pen
group by t1.Name
) a
where a.ct >= (select count(Pen) from cte_in)

Select Parent Record With All Children in SQL

Let's say I have two tables, "Parent" and "Child". Parent-to-Child is a many:many relationship, implemented through a standard cross-referencing table.
I want to find all records of Parent that are referenced by ALL members of a given set of Child using SQL (in particular MS SQL Server's T-SQL; 2005 syntax is acceptable).
For example let's say I have:
List item
Parent Alice
Parent Bob
Child Charlie references Alice, Bob
Child David references Alice
Child Eve references Bob
My goals are:
If I have Children Charlie, I want the result set to include Alice and Bob
If I have Children Charlie and David, I want the result set to include Alice and NOT Bob.
If I have Children Charlie, David, and Eve, I want the result set to include nobody.
Relying on a numerical trick (where the number of parent-child links = the number of children, that parent is linked to all children):
SELECT Parent.ParentID, COUNT(*)
FROM Parent
INNER JOIN ChildParent
ON ChildParent.ParentID = Parent.ParentID
INNER JOIN Child
ON ChildParent.ChildID = Child.ChildID
WHERE <ChildFilterCriteria>
GROUP BY Parent.ParentID
HAVING COUNT(*) = (
SELECT COUNT(Child.ChildID)
FROM Child WHERE <ChildFilterCriteria>
)
Here's an answer.
SQL query: Simulating an "AND" over several rows instead of sub-querying
And here's a specific application of that to this problem.
SELECT * FROM Parents
WHERE ParentId in
(
SELECT ParentId FROM ChildParent
WHERE ChildId in
(
SELECT ChildId FROM Child
WHERE ChildName in ('Charlie', 'David')
)
GROUP BY ParentId
HAVING COUNT(*) = 2
)
( I guess where you said "Child Eve references Eve" you meant "Child Eve references Bob", right?)
I think I've got it... looks ugly... the secret is the double negation... that is, everyone for which it's true,, is the same as not anyone for which is false... (ok, I have troubles with my english, but I guess you understand what I mean)
select * from parent
parent_id name
--------------------------------------- --------------------------------------------------
1 alice
2 bob
select * from child
child_id name
--------------------------------------- --------------------------------------------------
1 charlie
2 david
3 eve
select * from parent_child
parent_id child_id
--------------------------------------- ---------------------------------------
1 1
2 1
1 2
2 3
select * from parent p
where not exists(
select * from child c
where
c.child_id in ( 1, 2, 3 ) and
not exists(
select * from parent_child pc where
pc.child_id = c.child_id and
pc.parent_id = p.parent_id
)
)
--when child list = ( 1 )
parent_id name
--------------------------------------- --------------------------------------------------
1 alice
2 bob
--when child list = ( 1, 2 )
parent_id name
--------------------------------------- --------------------------------------------------
1 alice
--when child list = ( 1, 2, 3 )
parent_id name
--------------------------------------- --------------------------------------------------
well, I hope it helps...