join 4 tables in Postgres - sql

I have four main tables:
CREATE TABLE t_users (
user_id varchar PRIMARY KEY,
user_email varchar
);
CREATE TABLE t_items (
item_id varchar PRIMARY KEY,
owner_id varchar not null references t_users(user_id),
title varchar
);
CREATE TABLE t_access_items_users (
access_iu_id varchar PRIMARY KEY,
item_id varchar not null references t_items(item_id),
user_id varchar not null references t_users(user_id)
);
CREATE TABLE t_friends (
friend_id varchar PRIMARY KEY,
from_user_id varchar not null references t_users(user_id),
to_user_id varchar not null references t_users(user_id)
);
With data:
INSERT INTO t_users VALUES ('us123', 'us123#email.com');
INSERT INTO t_users VALUES ('us456', 'us456#email.com');
INSERT INTO t_users VALUES ('us789', 'us789#email.com');
INSERT INTO t_users VALUES ('public', 'public#email.com');
INSERT INTO t_items VALUES ('it123', 'us123', 'title1');
INSERT INTO t_items VALUES ('it456', 'us456', 'title2');
INSERT INTO t_items VALUES ('it678', 'us789', 'title3');
INSERT INTO t_items VALUES ('it323', 'us123', 'title4');
INSERT INTO t_items VALUES ('it764', 'us456', 'title5');
INSERT INTO t_items VALUES ('it826', 'us789', 'title6');
INSERT INTO t_items VALUES ('it568', 'us123', 'title7');
INSERT INTO t_items VALUES ('it038', 'us456', 'title8');
INSERT INTO t_items VALUES ('it728', 'us789', 'title9');
INSERT INTO t_access_items_users VALUES ('aiu123', 'it123', 'us123');
INSERT INTO t_access_items_users VALUES ('aiu456', 'it456', 'us456');
INSERT INTO t_access_items_users VALUES ('aiu678', 'it678', 'us789');
INSERT INTO t_access_items_users VALUES ('aiu323', 'it323', 'us123');
INSERT INTO t_access_items_users VALUES ('aiu764', 'it764', 'us456');
INSERT INTO t_access_items_users VALUES ('aiu826', 'it826', 'us789');
INSERT INTO t_access_items_users VALUES ('aiu568', 'it568', 'us123');
INSERT INTO t_access_items_users VALUES ('aiu038', 'it038', 'us456');
INSERT INTO t_access_items_users VALUES ('aiu728', 'it728', 'us789');
INSERT INTO t_access_items_users VALUES ('aiu728', 'it728', 'us789');
INSERT INTO t_access_items_users VALUES ('apu123', 'it678', 'public');
INSERT INTO t_access_items_users VALUES ('apu222', 'it123', 'public');
INSERT INTO t_access_items_users VALUES ('apu111', 'it456', 'public');
INSERT INTO t_access_items_users VALUES ('aiu333', 'it728', 'public');
INSERT INTO t_access_items_users VALUES ('aiu444', 'it826', 'public');
INSERT INTO t_friends VALUES ('f123', 'us123', 'us456');
Request for a type:
select *
from t_access_items_users
inner join t_items on t_access_items_users.item_id = t_items.item_id
inner join t_users on t_access_items_users.user_id = t_users.user_id
where t_access_items_users.user_id = 'public';
returns 5 rows.
Why the query type:
select
t_items.item_id,
t_items.owner_id,
t_items.title
from t_access_items_users
inner join t_items on t_access_items_users.item_id = t_items.item_id
inner join t_friends on t_items.owner_id = t_friends.from_user_id
where t_friends.to_user_id = 'us456'
or t_access_items_users.user_id = 'public';
returns 4 rows?
How to make the right request to get all need rows?
Ultimately I want to get data where the items have access to the public and with the user's friends' friendships.
How to make a query returning all the elements that:
t_friends.to_user_id = 'us456'
or t_access_items_users.user_id = 'public'
Thank you.

Related

Removing count column from query output

select top (5) t3.Model, t3.Manufacturer, t1.Colour, t1.RegistrationNumber, t1.DailyRentalPrice, count(t2.TruckID) as RentedAmount
from [IndividualTruck-PB] t1
inner join [TruckRental-PB] t2 on t1.TruckID = t2.TruckID
inner join [TruckModel-PB] t3 on t1.TruckModelID = t3.ModelID
group by t3.Model, t3.Manufacturer, t1.Colour, t1.RegistrationNumber, t1.DailyRentalPrice
order by RentedAmount desc
Bsically, I'm trying to get the top 5 most rented but don't want the actual count column as output only as a means of ordering the output. Is this possible?
You can try remove the count column and give the formula to order by part:
select top (5) t3.Model, t3.Manufacturer, t1.Colour, t1.RegistrationNumber, t1.DailyRentalPrice
from [IndividualTruck-PB] t1
inner join [TruckRental-PB] t2 on t1.TruckID = t2.TruckID
inner join [TruckModel-PB] t3 on t1.TruckModelID = t3.ModelID
group by t3.Model, t3.Manufacturer, t1.Colour, t1.RegistrationNumber, t1.DailyRentalPrice
order by count(t2.TruckID) desc
My test:
create table A (
col1 varchar(255)
);
insert into A (col1) values ('A');
insert into A (col1) values ('A');
insert into A(col1) values ('A');
insert into A(col1) values ('A');
insert into A(col1) values ('A');
insert into A(col1) values ('A');
insert into A(col1) values ('A');
insert into A(col1) values ('B');
insert into A(col1) values ('B');
insert into A(col1) values ('B');
insert into A(col1) values ('B');
insert into A(col1) values ('B');
insert into A(col1) values ('B');
insert into A(col1) values ('C');
insert into A(col1) values ('C');
insert into A(col1) values ('C');
insert into A(col1) values ('C');
insert into A(col1) values ('C');
insert into A(col1) values ('D');
insert into A(col1) values ('D');
insert into A(col1) values ('D');
insert into A(col1) values ('D');
insert into A(col1) values ('D');
insert into A(col1) values ('E');
insert into A(col1) values ('E');
insert into A(col1) values ('E');
Select for MS SQL Server 2017:
select top(2) col1 from A group by col1 order by count(col1) desc;
Output:
col1
A
B

How can I fix the error in duplicate tuples and sort correctly?

Can anyone help me understand the error?
My query:
select s1.name, s1.grade, s2.name, s2.grade
from student s1, student s2, likes l1, likes l2
where (s1.id_student = l1.id_student1 and s2.id_student = l1.id_student2)
and (s2.id_student = l2.id_student1 and s1.id_student = l2.id_student2)
and s1.name <= s2.name
order by s1.name, s2.name;
Return:
Cassandra|9 Gabriel|9
Gabriel 11|Gabriel|9
Gabriel|9|Gabriel|11
Jessica 11|Kyle|12
But the correct thing would be [exactly in that order]:
Cassandra|9|Gabriel|9
Jessica|11|Kyle|12
Gabriel|9|Gabriel|11
RS3 - For every pair of students who both like each other, return the name and grade of both students. Include each pair only once, with the two names in alphabetical order.
CREATE SCHEMA socialnetworkschema;
/* Create the schema for our tables */
SET SEARCH_PATH=socialnetworkschema;
CREATE TABLE socialnetworkschema.student (
id_student SERIAL PRIMARY KEY,
name varchar(255) DEFAULT NULL,
grade int DEFAULT NULL
);
CREATE TABLE socialnetworkschema.friend (
id_student1 int DEFAULT NULL,
id_student2 int DEFAULT NULL,
FOREIGN KEY (id_student1) REFERENCES socialnetworkschema.student (id_student) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (id_student2) REFERENCES socialnetworkschema.student (id_student) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE socialnetworkschema.likes (
id_student1 int DEFAULT NULL,
id_student2 int DEFAULT NULL,
FOREIGN KEY (id_student1) REFERENCES socialnetworkschema.student (id_student) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (id_student2) REFERENCES socialnetworkschema.student (id_student) ON DELETE CASCADE ON UPDATE CASCADE
);
/* Populate the tables with our data */
insert into socialnetworkschema.student values (1689, 'Gabriel', 9);
insert into socialnetworkschema.student values (1510, 'Jordan', 9);
insert into socialnetworkschema.student values (1381, 'Tiffany', 9);
insert into socialnetworkschema.student values (1709, 'Cassandra', 9);
insert into socialnetworkschema.student values (1101, 'Haley', 10);
insert into socialnetworkschema.student values (1782, 'Andrew', 10);
insert into socialnetworkschema.student values (1468, 'Kris', 10);
insert into socialnetworkschema.student values (1641, 'Brittany', 10);
insert into socialnetworkschema.student values (1247, 'Alexis', 11);
insert into socialnetworkschema.student values (1316, 'Austin', 11);
insert into socialnetworkschema.student values (1911, 'Gabriel', 11);
insert into socialnetworkschema.student values (1501, 'Jessica', 11);
insert into socialnetworkschema.student values (1304, 'Jordan', 12);
insert into socialnetworkschema.student values (1025, 'John', 12);
insert into socialnetworkschema.student values (1934, 'Kyle', 12);
insert into socialnetworkschema.student values (1661, 'Logan', 12);
insert into socialnetworkschema.friend values (1510, 1381);
insert into socialnetworkschema.friend values (1510, 1689);
insert into socialnetworkschema.friend values (1689, 1709);
insert into socialnetworkschema.friend values (1381, 1247);
insert into socialnetworkschema.friend values (1709, 1247);
insert into socialnetworkschema.friend values (1689, 1782);
insert into socialnetworkschema.friend values (1782, 1468);
insert into socialnetworkschema.friend values (1782, 1316);
insert into socialnetworkschema.friend values (1782, 1304);
insert into socialnetworkschema.friend values (1468, 1101);
insert into socialnetworkschema.friend values (1468, 1641);
insert into socialnetworkschema.friend values (1101, 1641);
insert into socialnetworkschema.friend values (1247, 1911);
insert into socialnetworkschema.friend values (1247, 1501);
insert into socialnetworkschema.friend values (1911, 1501);
insert into socialnetworkschema.friend values (1501, 1934);
insert into socialnetworkschema.friend values (1316, 1934);
insert into socialnetworkschema.friend values (1934, 1304);
insert into socialnetworkschema.friend values (1304, 1661);
insert into socialnetworkschema.friend values (1661, 1025);
insert into socialnetworkschema.friend select id_student2, id_student1 from friend;
insert into socialnetworkschema.likes values(1689, 1709);
insert into socialnetworkschema.likes values(1709, 1689);
insert into socialnetworkschema.likes values(1782, 1709);
insert into socialnetworkschema.likes values(1911, 1247);
insert into socialnetworkschema.likes values(1247, 1468);
insert into socialnetworkschema.likes values(1641, 1468);
insert into socialnetworkschema.likes values(1316, 1304);
insert into socialnetworkschema.likes values(1501, 1934);
insert into socialnetworkschema.likes values(1934, 1501);
insert into socialnetworkschema.likes values(1025, 1101);
insert into socialnetworkschema.likes values(1689, 1911);
insert into socialnetworkschema.likes values(1911, 1689);
First of all, you should consider rewriting your query to use explicit modern joins. After this, your logic for connecting two student records needs to change a bit.
with cte as (
select
s1.name as name1, s1.grade as grade1, s2.name as name2, s2.grade as grade2,
row_number() over (partition by least(s1.name, s2.name), greatest(s1.name, s2.name) order by s1.name) rn
from student s1
inner join likes l1
on s1.id_student = l1.id_student1
inner join student s2
on l1.id_student2 = s2.id_student
inner join likes l2
on l2.id_student1 = s2.id_student and
l2.id_student2 = s1.id_student
where s1.id_student <> s2.id_student
order by s1.name, s2.name
)
select name1, grade1, name2, grade2
from cte
where rn = 1;
Demo
Since your requirement is to find all students who mutually liked each other, we approach this by making a round trip to and from the student table. The likes table is what is known as a junction table in SQL. It exists to represent a one directional liked from one student to another. We join as follows:
student -> likes -> student -> likes ---
^ |
|___________________________________|
That is, a mutual like means that there exists a path we can find matching the above cycle.

SQL Finding multiple combinations in 2 tables (with all records)

I have two tables, one with some user configurations (#USERCONFIG) and the other (#COMBINATIONS), multiples combinations of configurations I need to find in the first table.
CREATE TABLE #COMBINATIONS (INDEX1 INT, MENU CHAR(10))
CREATE TABLE #USERCONFIG (USERID VARCHAR(10), MENU VARCHAR(10))
INSERT INTO #COMBINATIONS VALUES (1, 'ABC300')
INSERT INTO #COMBINATIONS VALUES (1, 'ABC400')
INSERT INTO #COMBINATIONS VALUES (2, 'ABC100')
INSERT INTO #COMBINATIONS VALUES (2, 'ABC500')
INSERT INTO #COMBINATIONS VALUES (2, 'ABC600')
INSERT INTO #USERCONFIG VALUES ('SMITHJ', 'ABC100')
INSERT INTO #USERCONFIG VALUES ('SMITHJ', 'ABC500')
INSERT INTO #USERCONFIG VALUES ('SMITHJ', 'ABC600')
INSERT INTO #USERCONFIG VALUES ('SMITHC', 'ABC100')
INSERT INTO #USERCONFIG VALUES ('SMITHC', 'ABC500')
INSERT INTO #USERCONFIG VALUES ('SMITHA', 'ABC100')
INSERT INTO #USERCONFIG VALUES ('SMITHA', 'ABC200')
INSERT INTO #USERCONFIG VALUES ('SMITHA', 'ABC300')
INSERT INTO #USERCONFIG VALUES ('SMITHA', 'ABC400')
INSERT INTO #USERCONFIG VALUES ('SMITHA', 'ABC600')
With this example data, I want the resultset to look like this:
'SMITHJ', '2'
'SMITHA', '1'
'SMITHC', '2'
Where it will return all users that have a match of configurations from the combinations table.
Any help would be appreciated.
The following will list users and the complete combinations they have. If it helps, you can think of it as the recipe-ingredient and user-ingredient textbook problem:
SELECT alluser.USERID, index_menu.INDEX1
FROM (SELECT DISTINCT USERID FROM #USERCONFIG) AS alluser
CROSS JOIN #COMBINATIONS AS index_menu
LEFT JOIN #USERCONFIG AS user_menu ON alluser.USERID = user_menu.USERID AND index_menu.MENU = user_menu.MENU
GROUP BY alluser.USERID, index_menu.INDEX1
HAVING COUNT(index_menu.MENU) = COUNT(user_menu.MENU)
This snippet will get that result:
IF OBJECT_ID('tempdb..#COMBINATIONS') IS NOT NULL DROP TABLE #COMBINATIONS;
IF OBJECT_ID('tempdb..#USERCONFIG') IS NOT NULL DROP TABLE #USERCONFIG;
CREATE TABLE #COMBINATIONS (INDEX1 INT, MENU VARCHAR(10));
CREATE TABLE #USERCONFIG (USERID VARCHAR(10), MENU VARCHAR(10));
INSERT INTO #COMBINATIONS (INDEX1, MENU) VALUES
(1, 'ABC301'),
(1, 'ABC401'),
(2, 'ABC102'),
(2, 'ABC502'),
(2, 'ABC602');
INSERT INTO #USERCONFIG (USERID, MENU) VALUES
('SMITHJ', 'ABC102'),
('SMITHJ', 'ABC502'),
('SMITHJ', 'ABC602'),
('SMITHC', 'ABC102'),
('SMITHC', 'ABC502'),
('SMITHA', 'ABC102'),
('SMITHA', 'ABC200'),
('SMITHA', 'ABC301'),
('SMITHA', 'ABC401'),
('SMITHA', 'ABC602');
SELECT USERID, INDEX1
FROM
(
SELECT uconf.USERID, comb.INDEX1,
COUNT(*) AS Total,
DENSE_RANK() OVER (PARTITION BY uconf.USERID ORDER BY COUNT(*) DESC, comb.INDEX1 ASC) AS Rnk
FROM #USERCONFIG uconf
INNER JOIN #COMBINATIONS comb
ON comb.MENU = uconf.MENU
GROUP BY uconf.USERID, INDEX1
) q
WHERE Rnk = 1
ORDER BY Total DESC, USERID;
Returns:
USERID INDEX1
SMITHJ 2
SMITHA 1
SMITHC 2

SQL Query JOIN on the same table for a given data

I have the following table and data:
CREATE TABLE TEST_TABLE (
ID NUMBER(6) NOT NULL,
COMMON_SEQ NUMBER(22),
NAME VARCHAR(20),
CONSTRAINT PK_CONST PRIMARY KEY (ID)
);
INSERT INTO TEST_TABLE (ID, COMMON_SEQ, NAME) VALUES (1001, NULL, 'Michelle');
INSERT INTO TEST_TABLE (ID, COMMON_SEQ, NAME) VALUES (1002, NULL, 'Tiberius');
INSERT INTO TEST_TABLE (ID, COMMON_SEQ, NAME) VALUES (1003, NULL, 'Marigold');
INSERT INTO TEST_TABLE (ID, COMMON_SEQ, NAME) VALUES (1004, 999, 'Richmond');
INSERT INTO TEST_TABLE (ID, COMMON_SEQ, NAME) VALUES (1005, 999, 'Marianne');
INSERT INTO TEST_TABLE (ID, COMMON_SEQ, NAME) VALUES (1006, NULL, 'Valentin');
INSERT INTO TEST_TABLE (ID, COMMON_SEQ, NAME) VALUES (1007, 888, 'Juliette');
INSERT INTO TEST_TABLE (ID, COMMON_SEQ, NAME) VALUES (1008, NULL, 'Lawrence');
Some records in this table are related to each other by the common value of COMMON_SEQ (for example COMMON_SEQ of 999 relates Richmond and Marianne).
How can I select all names based on given ID as an input?
I tried joining table to itself (works ok when COMMON_SEQ is null). This example returns Michelle record:
SELECT T.ID, T.COMMON_SEQ,T.NAME
FROM TEST_TABLE T
LEFT JOIN TEST_TABLE T2 ON NOT T.COMMON_SEQ is NULL
AND T.COMMON_SEQ=T2.COMMON_SEQ AND T.ID<>T2.ID
WHERE T.ID=1001
But it doesn't bring back 2 records for ID 1004. This example returns only Richmond record (but I need to return also Marianne record):
SELECT T.ID, T.COMMON_SEQ,T.NAME
FROM TEST_TABLE T
LEFT JOIN TEST_TABLE T2 ON NOT T.COMMON_SEQ is NULL
AND T.COMMON_SEQ=T2.COMMON_SEQ AND T.ID<>T2.ID
WHERE T.ID=1004
How can I improve/rewrite the query to return Richmond and Marianne records when I supply only one ID value (either 1004 or 1005)?
You could use:
SELECT *
FROM TEST_TABLE t
WHERE COMMON_SEQ IN (SELECT COMMON_SEQ
FROM TEST_TABLE t1
WHERE t1.ID = 1004)
OR t.ID = 1004;
DBFiddle Demo
Passing the same parameter twice to handle NULL in COMMON_SEQ.
Try this
SELECT COALESCE (ty.id, tx.id) AS id,
COALESCE (ty.common_seq, tx.common_seq) AS common_seq,
COALESCE (ty.name, tx.name) AS name
FROM test_table tx LEFT OUTER JOIN test_table ty
ON (tx.common_seq = ty.common_seq)
WHERE tx.ID = 1004;
With this you can avoid using IN or EXISTS and this is likely to be more performant.

SQL Server Grouped results containing number and null values

I want to build a query which will return only the phase_ids that contain level_id values of BOTH numeric AND null
In the following example I would expect to return phase_id 1,3
DECLARE #tbl TABLE
(phase_id numeric(10,0) null,
type_id numeric(10,0) null,
level_id numeric(10,0) null)
INSERT #tbl VALUES (1,1,1)
INSERT #tbl VALUES (1,2,1)
INSERT #tbl VALUES (1,5,2)
INSERT #tbl VALUES (1,1,5)
INSERT #tbl VALUES (1,1,NULL)
INSERT #tbl VALUES (2,1,2)
INSERT #tbl VALUES (2,3,6)
INSERT #tbl VALUES (2,1,1)
INSERT #tbl VALUES (3,1,6)
INSERT #tbl VALUES (3,1,NULL)
SELECT * FROM #tbl
Thank you
count(*) counts all rows, count(level_id) counts non-null rows:
select phase_id
from tbl
group by phase_id
having count(*) <> count(level_id)
and count(level_id) > 0