how to left join tables without main one - sql

Has anybody an idea how to join below three tables into one? I have really no idea :|
create table A (id number, val varchar2(1));
create table B (id number, val varchar2(1));
create table C (id number, val varchar2(1));
insert into a (id, val) values (1, 'a');
insert into a (id, val) values (2, 'b');
insert into a (id, val) values (3, 'c');
insert into b (id, val) values (2, 'd');
insert into b (id, val) values (3, 'e');
insert into b (id, val) values (4, 'f');
insert into c (id, val) values (3, 'g');
insert into c (id, val) values (4, 'h');
insert into c (id, val) values (5, 'i');

Thanks :)
SELECT COALESCE (a.id, b.id, c.id), a.val, b.val, c.val
FROM a
FULL OUTER JOIN b ON a.id = b.id
FULL OUTER JOIN c ON b.id = c.id
order by 1

The simplest way to do what you want uses full join and using:
select *
from a full join
b
using (id) full join
c
using (id)
order by id;
Here is a db<>fiddle. This uses Oracle because of the varchar2().
Your

Related

SQL Server: Find the group of records existing in another group of records

I'm new to SQL Server and I searched for a solution to find, if a group is included in another group.
The query result should be grp_id 2 because 'A'+'B' is included in grp 3 and 5.
The result should be the grp_id of the the groups, that are included in other groups. With this result i´ll make an update of another table, joined with the grp_id.
The result should be:
+----+
| id |
+----+
| 2 |
+----+
I stuck in SQL because I do not find a solution to compare the groups. The idea was using bitwise comparison. But for that I had to add the value of each item in a field. I think there could be an easier way.
Thank you and best regards!
Eric
create table tmp_grpid (grp_id int);
create table tmp_grp (grp_id int, item_val nvarchar(10));
insert into tmp_grpid(grp_id) values (1);
insert into tmp_grpid(grp_id) values (2);
insert into tmp_grpid(grp_id) values (3);
insert into tmp_grpid(grp_id) values (4);
insert into tmp_grpid(grp_id) values (5);
--
insert into tmp_grp(grp_id, item_val) values (1, 'A');
insert into tmp_grp(grp_id, item_val) values (2, 'A');
insert into tmp_grp(grp_id, item_val) values (2, 'B');
insert into tmp_grp(grp_id, item_val) values (3, 'A');
insert into tmp_grp(grp_id, item_val) values (3, 'B');
insert into tmp_grp(grp_id, item_val) values (3, 'C');
insert into tmp_grp(grp_id, item_val) values (4, 'A');
insert into tmp_grp(grp_id, item_val) values (4, 'C');
insert into tmp_grp(grp_id, item_val) values (4, 'D');
insert into tmp_grp(grp_id, item_val) values (5, 'A');
insert into tmp_grp(grp_id, item_val) values (5, 'B');
insert into tmp_grp(grp_id, item_val) values (5, 'E');
Geez!
Technically speaking, group one is found in all other groups right? So, first a cross join to itself would be best with the condition that the values are the same AND that the groups are different, but before we do that we need to know how many items belong to each group so that's why we have the first select as a group that includes the count of elements per group, then join that with the cross join...Hope this helps.
select distinct dist_grpid
from
(select grp_id, count(*) cc from tmp_grp group by grp_id) g
inner join
(
select dist.grp_id dist_grpid, tmp_grp.grp_id, count(*) cc
from
tmp_grp dist
cross join tmp_grp
where
dist.item_val = tmp_grp.item_val and
dist.grp_id != tmp_grp.grp_id
group by
dist.grp_id,
tmp_grp.grp_id
) cj on g.grp_id = cj.dist_grpid and g.cc = cj.cc

Getting a single result on one table from criteria on multiple rows in another table

Imagine a Student table with the name and id of students at a school, and a Grades table that has grades on the form:
grade_id | student_id.
What I want to do is find all the students that match an arbitrary criteria of say "find all students that have grade A, grade B, but not C or D".
In a school situation a student could have several A's and B's, but for my particular problem they will allways have one or none of each grade.
Also, the tables i'm working on are huge (several million rows in each), but i only need to find say 10-20 on each query (the purpose of this is to find test data).
Thanks!
Change the table variables to your physical tables and this should help?
DECLARE #Students TABLE (
StudentId INT,
StudentName VARCHAR(50));
INSERT INTO #Students VALUES (1, 'Tom');
INSERT INTO #Students VALUES (2, 'Dick');
INSERT INTO #Students VALUES (3, 'Harry');
DECLARE #StudentGrades TABLE (
StudentId INT,
GradeId INT);
INSERT INTO #StudentGrades VALUES (1, 1);
INSERT INTO #StudentGrades VALUES (1, 1);
INSERT INTO #StudentGrades VALUES (1, 2);
INSERT INTO #StudentGrades VALUES (1, 3);
INSERT INTO #StudentGrades VALUES (2, 1);
INSERT INTO #StudentGrades VALUES (2, 2);
INSERT INTO #StudentGrades VALUES (3, 1);
INSERT INTO #StudentGrades VALUES (3, 1);
INSERT INTO #StudentGrades VALUES (3, 3);
INSERT INTO #StudentGrades VALUES (3, 4);
INSERT INTO #StudentGrades VALUES (3, 4);
DECLARE #Grades TABLE (
GradeId INT,
GradeName VARCHAR(10));
INSERT INTO #Grades VALUES (1, 'A');
INSERT INTO #Grades VALUES (2, 'B');
INSERT INTO #Grades VALUES (3, 'C');
INSERT INTO #Grades VALUES (4, 'D');
--Student/ Grade Summary
SELECT
s.StudentId,
s.StudentName,
g.GradeName,
COUNT(sg.GradeId) AS GradeCount
FROM
#Students s
CROSS JOIN #Grades g
LEFT JOIN #StudentGrades sg ON sg.StudentId = s.StudentId AND sg.GradeId = g.GradeId
GROUP BY
s.StudentId,
s.StudentName,
g.GradeName;
--Find ten students with A and B but not C or D
SELECT TOP 10
*
FROM
#Students s
WHERE
EXISTS (SELECT * FROM #StudentGrades sg WHERE sg.StudentId = s.StudentId AND sg.GradeId = 1) --Got an A
AND EXISTS (SELECT * FROM #StudentGrades sg WHERE sg.StudentId = s.StudentId AND sg.GradeId = 2) --Got a B
AND NOT EXISTS (SELECT * FROM #StudentGrades sg WHERE sg.StudentId = s.StudentId AND sg.GradeId IN (3, 4)); --Didn't get a C or D
Make sure all your id fields are indexed.
select *
from students s
where exists
(
select *
from grades g
where g.grade_id in (1, 2)
and g.student_id = s.student_id
)

Copying data from one table to another

I'm updating data by selecting data from table and inserting into another. However there are some constraints on the other table and I get this :
DETAIL: Key (entry_id)=(391) is duplicated.
I basically do this :
insert into table_tmp
select * from table_one
How can I skip insert when this key entry duplicate occurs?
Update I can't save this schema info on SQL fiddle but here it is :
CREATE TABLE table1
("entry_id" int, "text" varchar(255))
;
INSERT INTO table1
("entry_id", "text")
VALUES
(1, 'one'),
(2, 'two'),
(3, 'test'),
(3, 'test'),
(12, 'three'),
(13, 'four')
;
CREATE TABLE table2
("entry_id" int, "text" varchar(255))
;
Create unique index entry_id_idxs
on table2 (entry_id)
where text='test';
INSERT INTO table2
("entry_id", "text")
VALUES
(1, 'one'),
(2, 'two'),
(3, 'test'),
(3, 'test'),
(12, 'three'),
(13, 'four')
;
Error that I get if I try to build the schema
Insert using join that returns unmatched rows:
INSERT INTO table2
SELECT DISTINCT t1.*
FROM table1 t1
LEFT JOIN table2 t2 ON t2.entry_id = t1.entry_id
WHERE t2.entry_id IS NULL
Use this query - SQLFiddle Demo:
INSERT INTO table2
SELECT t1.* FROM table1 t1
WHERE NOT EXISTS (
SELECT entry_id
FROM table2 t2
WHERE t2.entry_id = t1.entry_id
)

Help with a SQL Query

My tables:
suggestions:
suggestion_id|title|description|user_id|status|created_time
suggestion_comments:
scomment_id|text|user_id|suggestion_id
suggestion_votes:
user_id|suggestion_id|value
Where value is the number of points assigned to a vote.
I'd like to be able to SELECT:
suggestion_id, title, the number of comments and the SUM of values for that suggestion.
sorted by SUM of values. LIMIT 30
Any ideas?
You may want to try using sub queries, as follows:
SELECT s.suggestion_id,
(
SELECT COUNT(*)
FROM suggestion_comments sc
WHERE sc.suggestion_id = s.suggestion_id
) num_of_comments,
(
SELECT SUM(sv.value)
FROM suggestion_votes sv
WHERE sv.suggestion_id = s.suggestion_id
) sum_of_values
FROM suggestions s;
Test case:
CREATE TABLE suggestions (suggestion_id int);
CREATE TABLE suggestion_comments (scomment_id int, suggestion_id int);
CREATE TABLE suggestion_votes (user_id int, suggestion_id int, value int);
INSERT INTO suggestions VALUES (1);
INSERT INTO suggestions VALUES (2);
INSERT INTO suggestions VALUES (3);
INSERT INTO suggestion_comments VALUES (1, 1);
INSERT INTO suggestion_comments VALUES (2, 1);
INSERT INTO suggestion_comments VALUES (3, 2);
INSERT INTO suggestion_comments VALUES (4, 2);
INSERT INTO suggestion_comments VALUES (5, 2);
INSERT INTO suggestion_comments VALUES (6, 3);
INSERT INTO suggestion_votes VALUES (1, 1, 3);
INSERT INTO suggestion_votes VALUES (2, 1, 5);
INSERT INTO suggestion_votes VALUES (3, 1, 1);
INSERT INTO suggestion_votes VALUES (1, 2, 4);
INSERT INTO suggestion_votes VALUES (2, 2, 2);
INSERT INTO suggestion_votes VALUES (1, 3, 5);
Result:
+---------------+-----------------+---------------+
| suggestion_id | num_of_comments | sum_of_values |
+---------------+-----------------+---------------+
| 1 | 2 | 9 |
| 2 | 3 | 6 |
| 3 | 1 | 5 |
+---------------+-----------------+---------------+
3 rows in set (0.00 sec)
UPDATE: #Naktibalda's solution is an alternative solution that avoids sub queries.
I was typing the same query as potatopeelings.
But there is an issue:
Resultset after joins contains M*N rows (M-number of comments, N-number of votes, not less than 1) for each suggestion.
To avoid that you have to count distinct comment ids and divide a sum of votes by number of comments.
SELECT
s.*,
COUNT(DISTINCT c.scommentid) AS comment_count,
SUM(v.value)/GREATEST(COUNT(DISTINCT c.scommentid), 1) AS total_votes
FROM suggestions AS s
LEFT JOIN suggestion_comments AS c ON s.suggestion_id = c.suggestion_id
LEFT JOIN suggestion_votes AS v ON s.suggestion_id = v.suggestion_id
GROUP BY s.suggestion_id
ORDER BY total_votes DESC
LIMIT 30

SQL CTE counting childs recursion

I'd like (using cte) to count children in table in that way to have at parent level number of all children including theirs children. Is there any sample available?
CREATE TABLE t_parent (id INT NOT NULL PRIMARY KEY, parentID INT NOT NULL)
INSERT
INTO t_parent
VALUES (1, 0)
INSERT
INTO t_parent
VALUES (2, 1)
INSERT
INTO t_parent
VALUES (3, 1)
INSERT
INTO t_parent
VALUES (4, 2)
INSERT
INTO t_parent
VALUES (5, 1)
INSERT
INTO t_parent
VALUES (6, 5)
INSERT
INTO t_parent
VALUES (7, 5);
WITH q AS
(
SELECT id, parentId
FROM t_parent
UNION ALL
SELECT p.id, p.parentID
FROM q
JOIN t_parent p
ON p.id = q.parentID
)
SELECT id, COUNT(*)
FROM q
GROUP BY
id