Order parent records by child record count - sql

I have a table that has a foreign key that points to the id column same table.
pages
=====
id integer primary key autoincrement
name text
parent integer
FOREIGN KEY(parent) REFERENCES pages(id)
When I do a select query is it possible to sort the results by the number of children records ?

You could join it with an aggregate query on the child records and sort according to that:
SELECT p.*
FROM pages p
JOIN (SELECT parent, COUNT(*) AS cnt
FROM pages
GROUP BY parent) c ON p.id = c.parent
ORDER BY c.cnt

You could compute the count of children with a correlated subquery and sort according to that:
SELECT id, name
FROM pages
ORDER BY (SELECT count(*)
FROM pages AS p2
WHERE p2.parent = pages.id);

Related

Postgres many to one relationship join multiple tables and select all rows, provided that at least one row matches some criterea

Suppose I have a schema something like
create table if not exists user (
id serial primary key,
name text not null
);
create table if not exists post (
id serial primary key,
user_id integer not null references user (id),
score integer not null
)
I want to run a query that selects a row from the user table by ID, and all the rows that reference it from the post table, provided that at least one row in the post table has a score of greater than some number n (e.g. 50). I'm not exactly sure how to do this though.
You can use window functions. Let me assume that post has a user_id column so the tables can be tied together:
select u.*
from user u join
(select p.*, max(score) over (partition by user_id) as max_score
from post p
) p
on p.user_id = u.id
where p.max_score > 50;
If you just wanted all scores, then aggregation with filtering might be sufficient:
select u.*, array_agg(p.score order by p.score desc)
from user u join
post p
) p
on p.user_id = u.id
group by u.id
having max(p.score) > 50;

Finding max count of product

I am trying to find max count of product. The result must only display the brands which have max number of products in it. Can anyone suggest a better way to display result.
Here are the table details:
create table Brand (
Id integer PRIMARY KEY,
Name VARCHAR(40) NOT NULL
)
create table Product (
ID integer PRIMARY KEY,
Name VARCHAR(40) NOT NULL,
Price integer NOT NULL,
BrandId integer NOT NULL,
CONSTRAINT BrandId FOREIGN KEY (BrandId)
REFERENCES Brand(Id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
I have used this SQL query below but I am seeing an error below. The result must display the list of the brands that have max number of products as compared to other brands.
select count(p.id) as count, b.name AS brand_name from product p join brand b on b.Id = p.BrandId
where count(p.id) = (select max(count) from product)
group by b.name
ERROR: aggregate functions are not allowed in WHERE
LINE 2: where count(p.id) = (select max(count) from product)
^
SQL state: 42803
Character: 105
In Postgres 13+, you can use FETCH WITH TIES:
select count(*) as count, b.name AS brand_name
from product p join
brand b
on b.Id = p.BrandId
group by b.name
order by count(*) desc
fetch first 1 row with ties;
In older versions you can use window functions.

Select MAX price of a book with JOIN (2 tables) in SQL Server?

I have 2 tables
CREATE TABLE BOOKS
(
numbk INT PRIMARY KEY IDENTITY,
nombk NVARCHAR(60),
_numrub INT FOREIGN KEY REFERENCES CLASSIFICATION(numrub)
)
CREATE TABLE TARIFER
(
_numbk INT FOREIGN KEY REFERENCES BOOKS(numbk),
_nomed NVARCHAR(60) FOREIGN KEY REFERENCES EDITEURS(nomed),
_date DATE,
price DECIMAL(20,2),
PRIMARY KEY (_numouv, _nomed)
)
The question is: how do I list all titles of books (nombk) that have the max price?
PS: TRAFIER has the price columns, and a foreign key from BOOKS which is _numbk
I tried this:
select
o.nombk, max(prix)
from
TARIFER tr, books o
where
o.numbk = tr._numbk
group by
o.nombk
This lists all, but when I execute this:
select max(prix)
from TARIFER tr, books o
where o.numbk = tr._numbk
It returns only the max price. I don't know why. Could someone please explain?
In SQL Server, you can use TOP (1) WITH TIES:
select top (1) with ties b.nombk, t.prix
from books b join
TARIFER t
on b.numbk = t._numbk
order by t.prix desc;
Why not use just a subquery the get the max(prix) and then use that one to list all records with that prix:
select o.nombk ,prix
from TARIFER tr , books o
where o.numbk = tr._numbk
and tr.prix in (select max(prix) from TARIFER tr)
Both queries to aggregation, but not at the same level:
The first query has group by o.nombk, so it generate one record per book, and gives you the maximum price of this book accross all tarifers.
The second query has no group by clause, hence it gives you the maximum price of all books over all tarifers.
If you want the book with the higher price, there is no need to aggregate: you can join and sort the results by price:
select top (1) with ties b.*, t.*
from books b
inner join join tarifer t on b.numbk = t._numbk
order by t.prix desc;
top (1) with ties gives you the first record; if there are several records with the same, top price, the query returns them all.

Count total number of rows where this.row is related with rows in another table

I have two simple tables, parents and children. I am trying to count the number of parents who have at least one child.
create table People(
id integer unique,
name varchar(120),
primary key (id)
);
create table children(
id integer unique,
name varchar(120),
parentId integer,
primary key(id),
foreign key (parentId) references People(id)
);
This is the code I tried but it gives me the total number of children instead:
select count(*)
from (people p join children ch on ch.parentid = p.id)
having count(ch.id) > 0;
I am trying to count the number of parents who have at least one children.
This should be as simple as:
SELECT COUNT(*)
FROM people p
WHERE EXISTS (SELECT 1 FROM children c WHERE c.parentid = p.id)
Using EXISTS is usually the most efficient way to check that something, well, exists.
You're close. You just need to make the check for children on a per-parent basis:
SELECT COUNT(*) AS parents_with_children
FROM (SELECT p.name, COUNT(c.id) AS num_children
FROM people p
JOIN children c ON c.parentid = p.id
GROUP BY p.name
HAVING COUNT(c.id) > 0) p
Demo on dbfiddle
SELECT COUNT(*),p.*
FROM People p JOIN children c ON c.parnetId=p.id
WHERE NOT c.parnetId IS NULL
GROUP BY (p.id)
(no need for having since it only joins existing children anyways)
select count(p.*)
from people p inner join children ch
on ch.parentid = p.id
You could try something like this,
SELECT COUNT(DISTINCT children.parentid)
FROM People
INNER JOIN children
ON children.parentid = people.id;
With EXISTS:
select count(distinct p.id) counter from people p
where exists (
select 1 from children
where parentid = p.id
)
or even better:
select count(distinct parentid) counter
from children
because all the info you need is in the table children, so just count the distinct values in column parentid

SubQuery returning Multiple rows

I have got Two Tables
Product (Id, Name, CCode)
Category (CCode, CatName) - No Primary Key
Insert Into ProductNew (DW_Prod_Id, ProdId, ProdName, CC, CName)
Select Dw_Prod_Id.Nextval, Id, Name, CCode,
(Select CatName
From Category cc, Product p
Where cc.CCode IN p.CatCode
Group By CatName )
From Product;
SQL Error: ORA-01427: single-row subquery returns more than one row
01427. 00000 - "single-row subquery returns more than one row"
I am getting the above error Because my SubQuery returns more than one row.
I would like to Match the CatCode of each row from Product table to the Category Table so that I can obtain the CatName and then Insert rows into my New Table :)
if product can have only one category :
INSERT INTO ProdcutNew (DW_Prod_Id, ProdId, ProdName, CC, CName)
(SELECT Dw_Prod_Id.Nextval, p.Id, p.Name, cc.CCode, cc.CName
FROM Product p
INNER JOIN Category cc on p.CatCode = cc.CCode)
And you can correct your table name
ProdcutNew
to ProductNew ;)
EDIT :
But if, as #Gordon Linoff pointed, you have duplicates CCode, this won't work.
If you don't want a primary key on Category table, add at least a unique constraint (you'll have to clean your datas first)
ALTER TABLE Category ADD CONSTRAINT Unique_code UNIQUE(CCode);
EDIT 2 :
But the proper way would be :
Add an Id in Category as PK, and use it as Category_ID FK in Product (if CCode can change)
With the unique constraint on CCode.
You appear to have dulicates in your category table; otherwise a simple join would suffice:
select p.*, c.ccode
from Category c join
Product p
on c.ccode = p.catcode
To choose one category arbitrarily, do something like:
select p.*, c.ccode
from (select c.*
from (select c.*, row_number() over (partition by c.ccode order by c.ccode) as seqnum
from Category c
) c
where seqnum = 1
) c join
Product p
on c.ccode = p.catcode