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)
Related
I have a table test as following :
test1
Name NumbreMission
John 1
John 2
John 3
Jack 1
Jack 2
Jack 3
Jack 4
Jack 5
I am looking to get the following result : select max(NumbreMission )
Name NumbreMission
John
John
John 3
Jack
Jack
Jack
Jack
Jack 5
Any idea?
Any help would be the most appreciated ?
You can use a CTE to get the max for each name, and then join that to the original table:
with CTE as
(select
name,
max(numbremission) as maxnum
from
test1
group by name)
select
t1.name,
cte.maxnum
from
test1 t1
left outer join cte
on t1.name = cte.name
and t1.numbremission = cte.maxnum
SQLFiddle
The rank() function should do the trick, with a bit of jimmy-rigging:
SELECT Name,
CASE WHEN rk = 1
THEN NumberMission
ELSE NULL
END AS NumberMission
FROM (SELECT Name,
NumberMisson,
RANK() OVER(PARTITION BY Name ORDER BY NumberMission DESC) AS rk
FROM test1) t
ID UserId Name Amount RewardId
----------------------------
1 1 James 10.00 1
2 1 James 10.00 2
3 1 James 10.00 3
4 2 Dave 20.00 1
5 2 Dave 20.00 3
6 3 Lim 15.00 2
I'm trying to insert to another table, and this is the result that i'm struggling with:
Tbl1ID RewardId
------------------
1 1
1 2
1 3
4 1
4 3
6 2
I'm trying to get the MIN(ID) of each person and select all the RewardId that belong to that person.
You could do a simple self join to get the minimum id value per userid/rewardid combination;
SELECT MIN(a.id) Tbl1ID, b.RewardId
FROM mytable a
JOIN mytable b
ON a.name = b.name
GROUP BY b.userid, b.rewardid
ORDER BY tbl1id, rewardid;
An SQLfiddle to test with.
If you are running SQL Server 2008+, you can simplify it by using Window Function.
INSERT INTO AnotherTable (Tbl1ID, RewardID)
SELECT MIN(ID) OVER (PARTITION BY Name),
RewardID
FROM SourceTable
SQLFiddle Demo
Try this
SELECT tbl1id,RewardID From
table1 S JOIN
(
SELECT MIN(ID) as tbl1id,Name FROM table1 GROUP BY Name
) T ON T.Name = S.Name
ORDER BY tbl1id
FIDDLE DEMO
Output:
Tbl1ID RewardId
----------------
1 1
1 2
1 3
4 1
4 3
6 2
If you want insert into new table then try this out
Insert into Newtable (tbl1id,RewardID)
SELECT tbl1id,RewardID from
table1 S JOIN
(
SELECT MIN(ID) as tbl1id,Name
FROM table1
GROUP BY Name
) T ON T.Name = S.Name
ORDER BY tbl1id;
FIDDLE DEMO
Best explained using an example. My trials and results are bellow the example.
There are two tables (in reality I have multiple tables)
TABLE: Products
ID name
-----------
1 apple
2 orange
3 pear
TABLE: ATTRIBUTES
ID prod_ID attr_id value
----------------------------
1 1 101 20
2 1 102 red
3 1 103 sweet
4 2 101 30
5 2 102 orange
6 2 103 sour
6 3 101 40
7 3 102 green
8 3 103 sweet
DESIRED OUTPUT
name attr_id 101 AS 'price' attr_id 102 AS 'taste'
------------------------------------------------------
apple 20 sweet
orange 30 sour
pear 40 sweet
I have managed SQL till now but recently I have had to call 3 tables and combine column values like shown above. I just can't get my head wrapped around this. Help would be greatly appreciated.
Since you have not mentioned any RDBMS in your question, the query below will work on most RDBMS.
SELECT a.Name,
MAX(CASE WHEN b.attr_ID = 101 THEN b.value END) Price,
MAX(CASE WHEN b.attr_ID = 103 THEN b.value END) Taste
FROM Products a
INNER JOIN Attributes b
ON a.ID = b.prod_ID
GROUP BY a.Name
SQLFiddle Demo
You can use CASE statement for that: (This will work in MySQL)
SELECT p.name
,GROUP_CONCAT(CASE WHEN attr_id = 101 THEN value else NULL END) AS price
,GROUP_CONCAT(CASE WHEN attr_id = 102 THEN value else NULL END) AS color
,GROUP_CONCAT(CASE WHEN attr_id = 103 THEN value else NULL END) AS taste
FROM Products p JOIN Attributes a
ON p.id = a.prod_id
GROUP BY p.name;
Or you can also do the same with join: (This will work in both MySQL and SQL Server)
SELECT Name
,CASE WHEN a.attr_id = 101 THEN a.value ELSE NULL END AS price
,CASE WHEN b.attr_id = 102 THEN b.value else NULL END AS color
,CASE WHEN c.attr_id = 103 THEN c.value else NULL END AS taste
FROM Products p
LEFT JOIN Attributes a ON p.id = a.prod_id
LEFT JOIN Attributes b ON p.id = b.prod_id AND a.attr_id = 101 AND b.attr_id = 102
LEFT JOIN Attributes c ON p.id = c.prod_id AND a.attr_id = 101 AND c.attr_id = 103
WHERE a.attr_id IS NOT NULL and b.attr_id IS NOT NULL AND c.attr_id IS NOT NULL
I have also added a column for color.
Output
| NAME | PRICE | COLOR | TASTE |
-----------------------------------
| apple | 20 | red | sweet |
| orange | 30 | orange | sour |
| pear | 40 | green | sweet |
See this SQLFiddle
Demo for SQL Server
You have to query for each column separately...
SELECT
p.name,
(SELECT value FROM Attributes a WHERE attr_id=101 AND a.prod_ID=p.ID) AS price,
(SELECT value FROM Attributes a WHERE attr_id=102 AND a.prod_ID=p.ID) AS taste
FROM Products p
... or ...
If you are using MSSQL 2008 R2 you can use PIVOT:
http://msdn.microsoft.com/en-us/library/ms177410(v=sql.105).aspx
Using PIVOT, easier to adjust for extra attributes.
SELECT NAME, [101],[102],[103]
FROM (
SELECT P.NAME as NAME, A.ATTR_ID as ATTR_ID, A.VALUE as VALUE
FROM PRODUCTS as P, ATTRIBUTES as A
WHERE A.PROD_ID = P.ID
) SrcTable
PIVOT
(
MAX(VALUE)
for ATTR_ID in ([101],[102],[103])
) PivotTable;
Or a dynamic statement could be generated to adjust for a variable number of attributes (since this is the only thing that changes in the above statement)
The column string could be obtained with somethings like:
DECLARE #columns AS NVARCHAR(MAX);
set #columns = STUFF((SELECT distinct ',' + QUOTENAME(ATTR_ID)
FROM ATTRIBUTES
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),1,1,'');
Next, this could variable could be used to generate a string like the static PIVOT
DECALARE #stmt AS NVARCHAR(MAX);
set #stmt ='SELECT NAME, ' + #columns + '
FROM (
SELECT P.NAME as NAME, A.ATTR_ID as ATTR_ID, A.VALUE as VALUE
FROM PRODUCTS as P, ATTRIBUTES as A
WHERE A.PROD_ID = P.ID
) SourceTable
PIVOT (
MAX(VALUE)
for ATTR_ID in (' + #columns + ')
) PivotTable';
EXECUTE(#stmt);
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))
Given this data on SQL Server 2005:
SectionID Name
1 Dan
2 Dan
4 Dan
5 Dan
2 Tom
7 Tom
9 Tom
10 Tom
How would I select records where the sectionID must be +-2 or more from another section for the same name.
The result would be:
1 Dan
4 Dan
2 Tom
7 Tom
9 Tom
Thanks for reading!
SELECT *
FROM mytable a
WHERE NOT EXISTS
(SELECT *
FROM mytable b
WHERE a.Name = b.Name
AND a.SectionID = b.SectionID + 1)
Here's LEFT JOIN variant of Anthony's answer (removes consecutive id's from the results)
SELECT a.*
FROM mytable a
LEFT JOIN mytable b ON a.Name = b.Name AND a.SectionID = b.SectionID + 1
WHERE b.SectionID IS NULL
EDIT: Since there is another interpretation of the question (simply getting results where id's are more than 1 number apart) here is another attempt at an answer:
WITH alternate AS (
SELECT sectionid,
name,
EXISTS(SELECT a.sectionid
FROM mytable b
WHERE a.name = b.name AND
(a.sectionid = b.sectionid-1 or a.sectionid = b.sectionid+1)) as has_neighbour,
row_number() OVER (PARTITION by a.name ORDER BY a.name, a.sectionid) as row_no
FROM mytable a
)
SELECT sectionid, name
FROM alternate
WHERE row_no % 2 = 1 OR NOT(has_neighbour)
ORDER BY name, sectionid;
gives:
sectionid | name
-----------+------
1 | Dan
4 | Dan
2 | Tom
7 | Tom
9 | Tom
Logic: if a record has neighbors with same name and id+/-1 then every odd row is taken, if it has no such neighbors then it gets the row regardless if it is even or odd.
As stated in the comment the condition is ambiguous - on start of each new sequence you might start with odd or even rows and the criteria will still be satisfied with different results (even with different number of results).