Help with a SQL Query - sql

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

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

How to insert multiple row values into SQL

I have the following 3 tables:
CREATE TABLE Tests (
Test_ID INT,
TestName VARCHAR(50));
INSERT INTO Tests VALUES (1, 'SQL Test');
INSERT INTO Tests VALUES (2, 'C# Test');
INSERT INTO Tests VALUES (3, 'Java Test');
CREATE TABLE Users (
[User_ID] INT,
UserName VARCHAR(50));
INSERT INTO Users VALUES (1, 'Joe');
INSERT INTO Users VALUES (2, 'Jack');
INSERT INTO Users VALUES (3, 'Jane');
CREATE TABLE UserTests (
ID INT,
[User_ID] INT,
Test_ID INT,
Completed INT);
INSERT INTO UserTests VALUES (1, 1, 1, 0);
INSERT INTO UserTests VALUES (2, 1, 2, 1);
INSERT INTO UserTests VALUES (3, 1, 3, 1);
INSERT INTO UserTests VALUES (4, 2, 1, 0);
INSERT INTO UserTests VALUES (5, 2, 2, 0);
INSERT INTO UserTests VALUES (6, 2, 3, 0);
INSERT INTO UserTests VALUES (7, 3, 1, 1);
INSERT INTO UserTests VALUES (8, 3, 2, 1);
INSERT INTO UserTests VALUES (9, 3, 3, 1);
I would like to create some rule/trigger so that when a new user gets added to the Users table, an entry for each Test and that user's Id will get added to the UserTests table.
Something like this if the new user ID is 5:
INSERT dbo.UserTest
(USER_ID, TEST_ID, Completed)
VALUES
(5, SELECT TEST_ID FROM Tests, 0)
That syntax is of course wrong but to give an idea of what I expect to happen.
So I expect that statement to add these values to the UserTests table:
User ID| Test ID| Completed
5 | 1 | 0
5 | 2 | 0
5 | 3 | 0
You can use after trigger for user table.
Create Trigger tr_user on Users
After Insert
AS Begin
INSERT UserTest(USER_ID, TEST_ID, Completed)
Select I.USER_ID, t.TEST_ID, 0
From Inserted I, Tests t
END
Here's a SQL Fiddle that finds missing records and inserts them.
SQL Fiddle
The SELECT:
select u.user_id, t.test_id, 0 as Completed
from users u
cross join tests t
where not exists (
select 1
from usertests ut
where ut.user_id = u.user_id and ut.test_id = t.test_id)
Adding insert into UserTests (User_Id, Test_Id, Completed) before the select will insert these records.
You can add a user id on to the where clause to do it for a single user if required. It is re-runnable so it won't re-insert test ids for a user that already has them, but will add new ones if new tests are introduced.

SQL query for searching in the tree

I have a simple tree that has 4 level of deep data. Here is the table DDL
CREATE TABLE HIER_DEMO(
ID NUMBER,
LABEL VARCHAR2 (100),
PARENT_ID NUMBER)
Hierarchy starts WITH ID = PARENT_ID. Number of levels are fixed. It is always 4. We have leafs to all branches at 4th level. So we can also add 3 more columns that represent LABEL of ancestors if necessary.
I need to build a query that
Searches for particular phrase in LABEL on any level of hierarchy. For example LABEL LIKE '%MAGIC_WORD%'.
Returns all the nodes till leaf level under the hierarchy node that satisfies condition 1.
In addition we need to return all the ancestors of the hierarchy node that satisfies condition 1.
Here is an example
INSERT INTO HIER_DEMO VALUES (1, 'Mike', 1);
INSERT INTO HIER_DEMO VALUES (2, 'Arthur', 2);
INSERT INTO HIER_DEMO VALUES (3, 'Alex', 1);
INSERT INTO HIER_DEMO VALUES (4, 'Suzanne', 1);
INSERT INTO HIER_DEMO VALUES (5, 'Brian', 3);
INSERT INTO HIER_DEMO VALUES (6, 'Rick', 3);
INSERT INTO HIER_DEMO VALUES (7, 'Patrick', 4);
INSERT INTO HIER_DEMO VALUES (8, 'Simone', 4);
INSERT INTO HIER_DEMO VALUES (9, 'Tim', 5);
INSERT INTO HIER_DEMO VALUES (10, 'Andrew', 5);
INSERT INTO HIER_DEMO VALUES (11, 'Sandy', 6);
INSERT INTO HIER_DEMO VALUES (12, 'Brian', 6);
INSERT INTO HIER_DEMO VALUES (13, 'Chris', 7);
INSERT INTO HIER_DEMO VALUES (14, 'Laure', 7);
INSERT INTO HIER_DEMO VALUES (15, 'Maureen', 8);
INSERT INTO HIER_DEMO VALUES (16, 'Andy', 8);
INSERT INTO HIER_DEMO VALUES (17, 'Al', 2);
INSERT INTO HIER_DEMO VALUES (18, 'John', 17);
INSERT INTO HIER_DEMO VALUES (19, 'Frank', 18);
INSERT INTO HIER_DEMO VALUES (20, 'Tim', 19);
I am looking for the query that searches the tree for word 'Brian' in the LABEL column
The query should return these data
ID LABEL PARENT_ID
1 Mike 1
3 Alex 1
5 Brian 3
6 Rick 3
9 Tim 5
10 Andrew 5
12 Brian 6
Could somebody help with the Oracle query? We are using 11.2 version of Oracle database.
SQL> select * from HIER_DEMO
2 start with label like '%Brian%'
3 connect by prior id = parent_id
4 union
5 select * from HIER_DEMO
6 start with label like '%Brian%'
7 connect by prior parent_id = id and PRIOR parent_id != PRIOR id
8 /
ID LABEL PARENT_ID
---- -------------------- ---------
1 Mike 1
3 Alex 1
5 Brian 3
6 Rick 3
9 Tim 5
10 Andrew 5
12 Brian 6
We can use recursive CTE to accomplish this
WITH CTE1(ID, LABEL,PARENT_ID) AS
(
SELECT * FROM Hier_Demo
WHERE LABEL LIKE '%Brian%'
UNION ALL
SELECT h.ID, h.LABEL, h.PARENT_ID FROM Hier_Demo h
INNER JOIN CTE1 c
ON h.ID = c.PARENT_ID
WHERE h.ID <> h.PARENT_ID
),
CTE2(ID, LABEL,PARENT_ID) AS
(
SELECT * FROM Hier_Demo
WHERE LABEL LIKE '%Brian%'
UNION ALL
SELECT h.ID, h.LABEL, h.PARENT_ID FROM Hier_Demo h
INNER JOIN CTE2 c
ON h.PARENT_ID = c.ID
)
SELECT * FROM CTE2
UNION
SELECT * FROM CTE1
UNION
SELECT * FROM HIER_DEMO WHERE ID = 1
In the above code CTE1 gets records up in the hierarchy and CTE2 gets records down in the hierarchy of Brian, after that we just UNION the records returned by these CTEs
see the code working at SQLFiddle: http://sqlfiddle.com/#!4/0c99d/39
You can try this :
SELECT * FROM HIER_DEMO WHERE PARENT_ID=
(SELECT id FROM HIER_DEMO WHERE LABEL='Brian');

Filtering a bottom up recursive CTE with Sql Server 2005

I'm trying to query a hierarchy of data in a single database table from the bottom up (I don't want to include parents that don't have a particular type of child due to authorities). The schema and sample data are as follows:
create table Users(
id int,
name varchar(100));
insert into Users values (1, 'Jill');
create table nodes(
id int,
name varchar(100),
parent int,
nodetype int);
insert into nodes values (1, 'A', 0, 1);
insert into nodes values (2, 'B', 0, 1);
insert into nodes values (3, 'C', 1, 1);
insert into nodes values (4, 'D', 3, 2);
insert into nodes values (5, 'E', 1, 1);
insert into nodes values (6, 'F', 5, 2);
insert into nodes values (7, 'G', 5, 2);
create table nodeAccess(
userid int,
nodeid int,
access int);
insert into nodeAccess values (1, 1, 1);
insert into nodeAccess values (1, 2, 1);
insert into nodeAccess values (1, 3, 1);
insert into nodeAccess values (1, 4, 1);
insert into nodeAccess values (1, 5, 1);
insert into nodeAccess values (1, 6, 0);
insert into nodeAccess values (1, 7, 1);
with Tree(id, name, nodetype, parent)
as
(
select n.id, n.name, n.nodetype, n.parent
from nodes as n
inner join nodeAccess as na on na.nodeid = n.id
where na.access =1 and na.userid=1 and n.nodetype=2
union all
select n.id, n.name, n.nodetype, n.parent
from nodes as n
inner join Tree as t on t.parent = n.id
inner join nodeAccess as na on na.nodeid = n.id
where na.access =1 and na.userid=1 and n.nodetype=1
)
select * from Tree
Yields:
id name nodetype parent
4 D 2 3
7 G 2 5
5 E 1 1
1 A 1 0
3 C 1 1
1 A 1 0
How can I not include the duplicates in the result set? The queries against the real tables have many more nodes at the lowest levels and hence many more duplicates of the parent nodes. The solution needs to work with at least SQL Server 2005.
Thanks in advance!
The simplest (not necessarily the most efficient) solution:
...
)
SELECT DISTINCT id,name,nodetype,parent FROM Tree;
This changes the order from your sample output because the DISTINCT operator implements a sort. If there is some intentional ordering there I cannot detect it but you can add an ORDER BY if you know the order you want.

SQLite: How to count references to each ROWID in a table, then insert the count

I need some help writing a SQL statement. I have two tables in a SQLite database:
CREATE TABLE users (
user_id INTEGER PRIMARY KEY,
item_id INTEGER
);
CREATE TABLE items (
item_id INTEGER PRIMARY KEY,
ref_count INTEGER
);
I'm seeking a SQLite statement that will be like the following pseudocode:
for each row in items
items[ row ].ref_count = SELECT COUNT(users.item_id)
FROM users
WHERE users.item_id=row;
It looks like you can use the REPLACE command as follows:
REPLACE INTO items (item_id, ref_count)
SELECT item_id, COUNT(*) AS ref_count
FROM users
GROUP BY item_id;
Test case:
INSERT INTO users VALUES (1, 1);
INSERT INTO users VALUES (2, 1);
INSERT INTO users VALUES (3, 1);
INSERT INTO users VALUES (4, 2);
INSERT INTO users VALUES (5, 2);
INSERT INTO users VALUES (6, 3);
Result after running the REPLACE query:
SELECT * FROM items;
item_id ref_co
------------ ------
1 3
2 2
3 1
Adding some further items:
INSERT INTO users VALUES (7, 1);
INSERT INTO users VALUES (8, 4);
INSERT INTO users VALUES (9, 4);
And after re-running the REPLACE query:
SELECT * FROM items;
item_id ref_co
------------ ------
1 4
2 2
3 1
4 2