Get data from one table with nested relations - sql

I am new in DB and I have a table topics and in this table, I have a foreign key master_topic_id and this foreign key is related to the same table topics column id.
Schema:
CREATE TABLE public.topics (
id bigserial NOT NULL,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
published_at timestamp NULL,
master_topic_id int8 NULL,
CONSTRAINT t_pkey PRIMARY KEY (id),
CONSTRAINT t_master_topic_id_fkey FOREIGN KEY (master_topic_id) REFERENCES topics(id
);
I write a query - SELECT * FROM topics WHERE id = 10. But if this record has master_topic_id I need to get data by master_topic_id too.
I tried to do it by using JOIN, but join just concat records, but I need to have data from master_topic_id as new row.
Any help?

I think you are describing:
select t.*
from topics t
where t.id = 10 or
exists (select 1
from topics t2
where t2.master_topic_id = t.id and t2.id = 10
);
However, you might just want:
where 10 in (id, master_topic_id)

Use or in your where condition
SELECT *
FROM topics
WHERE id = 10
or master_topic_id = 10
you can use union all as well
SELECT *
FROM topics
WHERE id = 10
union all
SELECT *
FROM topics
WHERE master_topic_id = 10

Related

How to Make a mutually excusive select query in SQL?

I'm new to sql, and I need to write a query for a table that looks like this
CREATE TABLE TESTS
PATH_ID int PRIMARY KEY,
Day DATE NOT NULL,
Direction varchar(255) NOT NULL,
D_ID int NOT NULL,
FOREIGN KEY (D_ID) REFERENCES Drivers(D_ID),
);
INSERT INTO TESTS(PATH_ID,Day,Direction,D_ID)
VALUES (1,'2021-02-01' ,'Right',001),
(2,'2021-02-01' ,'Left',002),
(3,'2021-02-02','Right',002),
What I need to do is write a query that shows drivers (D_ID) who have ONLY ever gone Right (Direction), and show The D_ID, the Day, and all the times the driver went right.
One method is not exists:
select t.*
from tests t
where not exists (select 1
from tests t2
where t2.d_id = t.d_id and t2.direction <> 'Right'
);
you can use not in
select a.* from Tests a where D_ID not in (
select D_ID from Tests where direction <>'Right'
)

SQLITE3: converting IDs to codes when there are hundreds of columns to convert

I have a table A that has several hundred columns (let's say 301 for example) with the first column being the primary key and the rest being IDs from table B i.e.
CREATE TABLE "A" (
ko_index_id INTEGER NOT NULL,
ko1 INTEGER NOT NULL,
ko2 INTEGER NOT NULL,
...
ko300 INTEGER NOT NULL,
PRIMARY KEY (ko_index_id)
);
CREATE TABLE "B" (
id INTEGER NOT NULL,
name INTEGER NOT NULL,
PRIMARY KEY (id)
);
I would like to be able to convert the IDs into names. For example:
SELECT name FROM B WHERE id in (SELECT * FROM A);
Except the SELECT * part means that ko_index_id will be fed into B which is wrong. If there were only two columns in A I could just write
SELECT name FROM B WHERE id in (SELECT ko1, ko2 FROM A);
but table A has 300 columns!
Can anyone help me around this?
300+ columns? How about redoing table A by pivoting those columns into rows. You could have key name and value column. For example:
select * from A:
id, ko_name, ko_value
1, ko1, 5
1, ko2, 6
Then selecting the keys becomes much easier; e.g:
SELECT name FROM B WHERE id in (SELECT ko_value FROM A where ko_name in ('ko1', 'ko2')) ;
I agree with #Gordon's comment. If you can afford to change your data model, I would suggest you use an intersection table. It's the typical way to model "many-to-many" relationships in a database.
Example:
CREATE TABLE "A" (
id INTEGER NOT NULL,
...
PRIMARY KEY (id)
);
CREATE TABLE "B" (
id INTEGER NOT NULL,
name INTEGER NOT NULL,
...
PRIMARY KEY (id)
);
CREATE TABLE "AB" (
a_id INTEGER NOT NULL,
b_id INTEGER NOT NULL
);
SELECT A.id, B.name
FROM A
INNER JOIN AB ON A.id=AB.a_id
INNER JOIN B ON AB.b_id=B.id;

PostgreSQL SELECT JOIN

I have a problem with making a proper SELECT for my exercise:
There are two tables that I have created:
1. Customer
2. Order
ad. 1
CREATE TABLE public."Customer"
(
id integer NOT NULL DEFAULT nextval('"Customer_id_seq"'::regclass),
name text NOT NULL,
surname text NOT NULL,
address text NOT NULL,
email text NOT NULL,
password text NOT NULL,
CONSTRAINT "Customer_pkey" PRIMARY KEY (id),
CONSTRAINT "Customer_email_key" UNIQUE (email)
)
ad.2
CREATE TABLE public."Order"
(
id integer NOT NULL DEFAULT nextval('"Order_id_seq"'::regclass),
customer_id integer NOT NULL,
item_list text,
order_date date,
execution_date date,
done boolean DEFAULT false,
confirm boolean DEFAULT false,
paid boolean DEFAULT false,
CONSTRAINT "Order_pkey" PRIMARY KEY (id),
CONSTRAINT "Order_customer_id_fkey" FOREIGN KEY (customer_id)
REFERENCES public."Customer" (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Please do not mind how columns properties were set.
The problem I have is following:
How to make a SELECT query which will give me as a result ids and emails of customers who have ordered something after '2017-09-15'
I suppose that this should go with JOIN but none of the queries I tried have worked :/.
Thanks!
You should post the queries that you tried, but in the meantime try this. It's a simple join :
SELECT DISTINCT id
, email
FROM public."Customer" c
JOIN public."Order" o
ON c.id = o.customer_id
WHERE order_date > '2017-09-15'
In table "Order" you just need to add current constraint for customer id:
customer_id integer REFERENCES Customer (id)
for more information check this page:
https://www.postgresql.org/docs/9.2/static/ddl-constraints.html
So, the query should be like this:
SELECT id, email
FROM Customer
INNER JOIN Order
ON (Order.customer_id = Customer.id)
WHERE order_date >= '2017-09-15'
Also, the useful docs you can check: https://www.postgresql.org/docs/current/static/tutorial-join.html

How to query two link tables in the same query?

I have two tables. device that can have on blacklisted_device. I would like to get the number of device that include specific user_ids and in the same request number of blacklisted_devices linked.
Here the full sql to try it :
CREATE TABLE device (
device_id serial PRIMARY KEY,
user_id integer,
updated_at timestamp default current_timestamp
);
CREATE TABLE blacklisted_device (
blacklisted_id serial PRIMARY KEY,
device_id integer,
updated_at timestamp default current_timestamp,
CONSTRAINT blacklisted_device_device_id_fkey FOREIGN KEY (device_id)
REFERENCES device (device_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);
INSERT INTO device (user_id)
VALUES (1),(2),(2),(7),(88),(99),(102),(106);
INSERT INTO blacklisted_device (device_id)
VALUES (1),(2),(3),(4);
SELECT COUNT(*) AS total_device
FROM device
WHERE user_id IN (7,88,99);
SELECT COUNT(*) AS blacklisted
FROM blacklisted_device
WHERE device_id IN (SELECT device_id FROM device WHERE user_id IN (7,88,99));
As you can see at the end I get the result I want but in two requests. How to get it in one request?
total_device: 3, blacklisted: 1
Feel free to make any comment on all the SQL, I probably made few mistakes.
Thanks
You need a LEFT JOIN:
SELECT COUNT(*) AS total_device,
COUNT(DISTINCT bd.device_id) AS blacklisted
FROM device d
LEFT JOIN blacklisted_device bd
ON d.device_id = bd.device_id
WHERE d.user_id IN (7,88,99);

sql join on range giving double row for single record

I needed to join three tables Result, ResultITems and GradeScale. When i do, i get double or two of the same row. I tried Creating the records in sqlfiddle but i get a different correct result. The schema i used in creating the tables in my local sqlite db is exactly the same, which is shown here.
The result table
CREATE TABLE Result (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
SubjectID INTEGER REFERENCES Subjects ( ID ) ON DELETE CASCADE,
SessionID INT REFERENCES Sessions ( ID ),
TermID INT REFERENCES terms ( ID ),
ClassID INTEGER REFERENCES Classes ( ID )
);
The resultItems table
CREATE TABLE ResultItems (
StudentID INTEGER,
ResultID INTEGER REFERENCES Result ( ID ) ON DELETE CASCADE,
Total DECIMAL( 10, 2 )
);
And the gradescale table
CREATE TABLE gradeScale
(ID INTEGER PRIMARY KEY AUTOINCREMENT,
minscore tinyint NOT NULL,
maxscore tinyint NOT NULL,
grade char(1) NOT NULL,
ClassCatID INTEGER
);
now when i execute this query below, i et double row for each record in the ResultItems table
Select ri.studentid, ri.Total,g.grade
From ResultItems ri
left join GradeScale g
ON ( ri.total >= g.minscore AND ri.total <= g.maxscore )
left join Result r on r.id=ri.resultid
WHERE r.sessionid = 4
AND
r.termid = 1
AND
r.classid = 9
ORDER BY grade ASC;
Please see the picture below to see what i mean
![enter image description here][1]
and here is the sql fibble which i created http://sqlfiddle.com/#!7/ffb42/1
why am i getting double rows in the output when i execute in my local db?
From #JotaBet's help, i was able to trace the error to the GradeScale table wihci had double entries for each grade letter for each class group.
So i rewrote the sql to take notice of that
left join GradeScale g
ON ( AND c.classcatid = g.classcatid (ri.total >= g.minscore AND ri.total <= g.maxscore) )