Row Act like Column Database Query - sql

I have a Person table.Persons are good at some language and i give them weight.My table are as follows:
Person
ID Name Description
1 Rodra Some..
2 Rakib Some..
3 Samsad Some..
4 Foysal Some..
Language
TypeID TypeName
1 C#
2 Asp.Net
3 Python
4 JSP
5 Java
6 Jquery
7 Android
PersonSkill
ID PersonID TypeID Weight
1 1 1 60
2 1 3 50
3 1 7 40
4 2 1 80
5 2 2 70
6 3 1 90
7 3 2 50
8 4 1 60
9 4 2 50
10 4 6 40
11 4 7 55
Now i want to query those person who know c#(TypeId 1)>65 and Asp.net(TypeID 2)>65.How to do it?Anyone helps me greatly appreciated.

Select *
FROM person
WHERE EXISTS(SELECT * FROM PersonSkill WHERE PersonId = Person.Id AND TypeID = 1 AND Weight>65)
AND EXISTS(SELECT * FROM PersonSkill WHERE PersonId = Person.Id AND TypeID = 2 AND Weight>65)

select p.* from
person p
JOIN personSkill ps ON p.ID=ps.PersonID
JOIN Language l ON l.TypeID=ps.TypeID
WHERE (l.TypeName = 'c#' AND ps.weight>65)
AND
(l.TypeName = 'Asp.net' AND ps.weight>65)

Related

Cant merge two queries with different columns

I'm studying SQL and somehow I'm stuck with a question. I have 2 tables ('users' and 'follows').
Follows Table
user_id
follows
date
1
2
1993-09-01
2
1
1989-01-01
3
1
1993-07-01
2
3
1994-10-10
3
2
1995-03-01
4
2
1988-08-08
4
1
1988-08-08
1
4
1994-04-02
1
5
2000-01-01
5
1
2000-01-02
5
6
1986-01-10
7
1
1990-02-02
1
7
1996-10-01
1
8
1993-09-03
8
1
1995-09-01
8
9
1995-09-01
9
8
1996-01-10
7
8
1993-09-01
3
9
1996-05-30
4
9
1996-05-30
Users Table
user_id
first_name
last_name
school
1
Harry
Potter
Gryffindor
2
Ron
Wesley
Gryffindor
3
Hermonie
Granger
Gryffindor
4
Ginny
Weasley
Gryffindor
5
Draco
Malfoy
Slytherin
6
Tom
Riddle
Slytherin
7
Luna
Lovegood
Ravenclaw
8
Cho
Chang
Ravenclaw
9
Cedric
Diggory
Hufflepuff
I need to list all rows from follows where someone from one house follows someone from a different house. I tried to make 2 queries, one to get all houses related to follows.user_id and another one with all houses related to follows.follows and "merge" then:
select a.nome_id, a.user_id_house, b.follows_id, b.follows_house
from ( select follows.user_id as nome_id
, users.house as user_id_house
from follows inner join users
ON users.user_id = follows.user_id
) as a,
( select follows.follows as follows_id
, users.house as follows_house
from follows inner join users
ON follows.user_id = users.user_id
) as b
where a.user_id_house <> b.follows_house;
The problem is that the result is like 400 rows, its not right. I have no idea how I could solve this.
Try this
SELECT follows.user_id, users.school, followers.user_id, followers.school FROM follows
JOIN users ON follows.user_id=users.user_id
JOIN users as followers ON follows.follows=followers.user_id
WHERE users.school <> followers.school
Note: Pay attention to naming in my answer
Thanks for correcting to Thorsten Kettner

How to rewrite query which gives amount of specific value in row to avoid some values and count further with others?

I have a query which gives me amount of grade 5 for every student in row (if student don't have any other grade on the way):
select distinct on (student, class) scg.*
from (select student, class, grade, count(*) as cnt,
min(gradeDate), max(gradeDate), min_gradeDate, max_gradeDate
from (select t.*,
row_number() over (partition by student, class, grade order by gradeDate) as seqnum_scg,
row_number() over (partition by student, class order by gradeDate) as seqnum_sc
from t
) t
where grade = 5
group by student, class, grade, (seqnum_sc - seqnum_scg)
) scg
order by student, class, cnt desc;
The original problem is explained here:
How to count data with specific values and for specific user/person (in row)?
But now I want to extend this query with one more feature. This counter gives me max value unless some student have grade 4/3/2/1, but now I want it to:
stop counting if student has 4 or 3 grade and start over (with previous max) when student get another 5
What I mean:
Actual query: 5, 5, 5, 4, 3, 5, 5, 2 --> gives me max = 3
New query: 5, 5, 5, 4, 3, 5, 5, 2 --> gives me max = 5, because 4 and 3 stop counter and start it when user gets another 5
stop counting if student gets grade 2 or 1 (and give me max value before getting 2/1 grade) So the same thing which query does now for every grade except 5, but I want it only for 2 and lower (that I can specify in query).
Can someone help me rewrite the second query given by #Gordon Linoff to work like that and tell me what changed?
Edit: examples as requested:
id student grade class gradeDate
1 1 5 1 2017-03-03
2 1 5 1 2017-03-04
3 1 1 1 2017-03-05
4 1 5 1 2017-03-06
5 1 5 1 2017-03-07
6 1 5 1 2017-03-08
7 1 1 1 2017-03-09
8 2 5 2 2017-03-03
9 3 5 3 2017-03-03
10 4 5 4 2017-03-03
11 4 5 4 2017-03-04
12 4 4 4 2017-03-05
13 4 3 4 2017-03-06
14 4 5 4 2017-03-07
15 4 5 4 2017-03-08
16 5 5 5 2017-03-01
17 5 5 5 2017-03-03
18 5 5 5 2017-03-04
19 5 5 5 2017-03-05
20 5 5 5 2017-03-06
21 5 2 5 2017-03-07
22 5 5 5 2017-03-08
23 5 5 5 2017-03-09
Student one : max = 3
Student two : max = 1
Student three : max = 1
Student four : max = 4 (grade 4 and 3 stop counter, but don't reset it)
Student five : max = 5 (because grade 2 reset counter, lack of grade on date
2017-03-02 is not a problem for counter)
One of the methods can be using 2 subqueries and one analytic function
Demo: http://sqlfiddle.com/#!15/74b71/10
SELECT student, max( xxx )
FROM (
SELECT student, grp_nbr, count(CASE WHEN grade = 5 THEN 1 END) As xxx
FROM (
SELECT *,
SUM ( CASE WHEN grade in (1,2)
THEN 1 ELSE 0
END
) OVER (Partition by student Order By gradeDate ) As grp_nbr
FROM table1
) x
GROUP BY student, grp_nbr
) y
GROUP BY student
ORDER BY student
| student | max |
|---------|-----|
| 1 | 3 |
| 2 | 1 |
| 3 | 1 |
| 4 | 4 |
| 5 | 5 |

SQL: Create view from 2 tables printing null values when no records

I have in my DB these 2 tables:
LESSONS RATINGS
ID | NAME ID | LESSON | RATING
1 lesson1 1 1 4
2 lesson2 2 2 2
3 lesson3 3 1 5
4 lesson4 4 4 2
5 lesson5 5 3 1
6 lesson6 6 2 5
7 lesson7 7 6 3
And I want a View that show me something like this:
LESSONS_RATINGS
IDL| NAME | RATING
1 lesson1 4.5
2 lesson2 3.5
3 lesson3 1
4 lesson4 2
5 lesson5 NULL
6 lesson6 3
7 lesson7 NULL
But what I've been able to get so far is this:
LESSONS_RATINGS
IDL| NAME | RATING
1 lesson1 4.5
2 lesson2 3.5
3 lesson3 1
4 lesson4 2
6 lesson6 3
Notice that NULL records are missing. That's why in table RATINGS there are no records of lessons 5 and 7. I'm doing this:
CREATE OR REPLACE VIEW `LESSONS_RATINGS` AS
select
`l`.`ID` AS `IDL`,
`l`.`NAME` AS `NAME`,
CASE WHEN AVG(`lr`.`RATING`) IS NULL THEN NULL ELSE AVG(`lr`.`RATING`) END AS `RATING`
from
`LESSONS` AS `l`,
`RATINGS` AS `lr`
where
(`l`.`ID` = `lr`.`ID`)
group by `l`.`ID`;
Use an OUTER JOIN:
select
`l`.`ID` AS `IDL`,
`l`.`NAME` AS `NAME`,
AVG(`lr`.`RATING`) AS `RATING`
from
`LESSONS` AS `l` LEFT JOIN `RATINGS` AS `lr`
ON `l`.`ID` = `lr`.`ID`
group by `l`.`ID`;
Also, I don't think there is a need for the Case statement -- you can just use AVG(lr.rating).
A Visual Explanation of SQL Joins

select max id of each id

I have two tables: tbComment and tbCommentStatus:
**tbComment**
CommentID IsLocked
1 0
2 0
3 1
4 0
5 1
**tbCommentStatus**
CommentStatusID CommentID StatusTypeID
105 1 1
106 1 4
107 2 1
108 3 1
109 3 4
110 4 1
112 5 1
112 5 4
I want to return CommentIDs of a dataset of the highest CommentStatusIDs for each CommentID Where StatusTypeID = 4 and IsLocked = 1.
Basically, here I would return CommentIDs: 3,5 because their highest CommentStatusID has a StatusTypeID=4 and tbComment.IsLocked=1
Man, I hope this makes sense. If it doesn't I can clarify.
Thanks!
Try the following query.
SELECT c.CommentID, MAX(cs.CommentStatusID) MaxCommentStatusID
FROM tbComment c
JOIN tbCommentStatus cs ON c.CommentID = cs.CommentID
WHERE c.IsLocked = 1
AND cs.StatusTypeID = 4
GROUP BY c.CommentID

Query to multiply certain sets of rows on a single table

I've got a bit of a complicated query that I'm struggling with. You will notice that the schema isn't the easiest thing to work with but it's what I've been given and there isn't time to re-design (common story!).
I have rows like the ones below. Note: The 3 digit value numbers are just random numbers I made up.
id field_id value
1 5 999
1 6 888
1 7 777
1 8 foo <--- foo so we want the 3 values above
1 9 don't care
2 5 123
2 6 456
2 7 789
2 8 bar <--- bar so we DON'T want the 3 values above
2 9 don't care
3 5 623
3 6 971
3 7 481
3 8 foo <--- foo so we want the 3 values above
3 9 don't care
...
...
n 5 987
n 6 654
n 7 321
n 8 foo <--- foo so we want the 3 values above
n 9 don't care
I want this result:
id result
1 999*888*777
3 623*971*481
...
n 987*654*321
Is this clear? So we have a table with n*5 rows. For each of the sets of 5 rows: 3 of them have values we might want to multiply together, 1 of them tells us if we want to multiply and 1 of them we don't care about so we don't want the row in the query result.
Can we do this in Oracle? Preferably one query.. I guess you need to use a multiplication operator (somehow), and a grouping.
Any help would be great. Thank you.
something like this:
select m.id, exp(sum(ln(m.value)))
from mytab m
where m.field_id in (5, 6, 7)
and m.id in (select m2.id
from mytab m2
where m2.field_id = 8
and m2.value = 'foo')
group by m.id;
eg:
SQL> select * from mytab;
ID FIELD_ID VAL
---------- ---------- ---
1 5 999
1 6 888
1 7 777
1 8 foo
1 9 x
2 5 123
2 6 456
2 7 789
2 8 bar
2 9 x
3 5 623
3 6 971
3 7 481
3 8 foo
3 9 x
15 rows selected.
SQL> select m.id, exp(sum(ln(m.value))) result
2 from mytab m
3 where m.field_id in (5, 6, 7)
4 and m.id in (select m2.id
5 from mytab m2
6 where m2.field_id = 8
7 and m2.value = 'foo')
8 group by m.id;
ID RESULT
---------- ----------
1 689286024
3 290972773
Same logic; just removed the hard-coded values. posting this answer thinking might be helpful to some others.
SELECT a.id,
exp(sum(ln(a.val)))
FROM mytab a,
(SELECT DISTINCT id,
field_id
FROM mytab
WHERE val = 'foo') b
WHERE a.id = b.id
AND a.field_id < b.field_id
GROUP BY a.id;