How to order by sum in each rollup data group? - postgresql-9.5

I have my animal table with points for each animal, its color and name.
create table animals (
category text,
color text,
name text,
points numeric
)
insert into animals values ('CATS', 'WHITE', 'FLUFFY', 1234)
insert into animals values ('CATS', 'WHITE', 'FLUFFY', 2568)
insert into animals values ('CATS', 'WHITE', 'SALLY', 124)
insert into animals values ('CATS', 'WHITE', 'SALLY', 12401)
insert into animals values ('CATS', 'WHITE', 'MR KITTY', 101)
insert into animals values ('CATS', 'WHITE', 'MR KITTY', 1761)
insert into animals values ('CATS', 'BLACK', 'DARTH CATHER', 1761)
insert into animals values ('CATS', 'BLACK', 'DARTH CATHER', 2361)
insert into animals values ('CATS', 'BLACK', 'ITS A TRAP', 171)
insert into animals values ('CATS', 'BLACK', 'HAN SOLO', 1713)
insert into animals values ('DOGS', 'BROWN', 'PLUTO', 1761)
insert into animals values ('DOGS', 'BROWN', 'DUFFY', 23611)
insert into animals values ('DOGS', 'BROWN', 'PICACHOU', 1171)
insert into animals values ('DOGS', 'BROWN', 'DUFFY', 17123)
insert into animals values ('DOGS', 'BLACK', 'MICKEY', 17361)
insert into animals values ('DOGS', 'BLACK', 'JACKSON', 23361)
insert into animals values ('DOGS', 'BLACK', 'BA', 1751)
insert into animals values ('DOGS', 'BLACK', 'MACGUYVER', 17513)
insert into animals values ('FISH', 'YELLOW', 'LENON', 1571)
insert into animals values ('FISH', 'YELLOW', 'BEUSCONI', 17153)
I need to return sql output similar to output from Excel pivot table:
(with proper descending order by sum of points within data groups)
I'm really close with this query:
with data_grouped as
(
Select
COALESCE ( category, 'Total'::text) as category,
COALESCE ( color, 'Total'::text) as color,
COALESCE ( name,'Total'::text) as name,
sum(points) as points
FROM animals
GROUP BY category, color, name
),
data_evolutions as
(
select grouping(d.category, d.color, d.name) as GR_ALL,
d.category,
d.color,
d.name,
sum(d.points) as points
from data_grouped d
group by rollup(d.category, d.color, d.name)
order by category, color, name
)
select
COALESCE (d.category, 'Total'::text) as "CATEGORY",
COALESCE (d.color, 'Total'::text) as "COLOR",
COALESCE (d.name, 'Total'::text) as "NAME",
points
from data_evolutions d
order by
CASE WHEN category IS NULL THEN 1 ELSE 0 END, category,
CASE WHEN color IS NULL THEN 1 ELSE 0 END, color,
CASE WHEN name IS NULL THEN 1 ELSE 0 END, points desc
but still I get wrong order.
Animals are ordered by name, not by sum.
How should I change my query to get descending order by sum of points within each level?
I need correct sums, and order with totals and subtotals.

Related

How to create a column at psql after join two tables and multiply a quantity by row categories?

I've created two tables and inserted values, that's ok:
CREATE TABLE categories (
id numeric PRIMARY KEY,
name varchar
);
CREATE TABLE products (
id numeric PRIMARY KEY,
name varchar(50),
amount numeric,
price numeric(7,2),
id_categories numeric REFERENCES categories (id)
);
INSERT INTO categories (id, name)
VALUES
(1, 'wood'),
(2, 'luxury'),
(3, 'vintage'),
(4, 'modern'),
(5, 'super luxury');
INSERT INTO products (id, name, amount, price, id_categories)
VALUES
(1, 'Two-doors wardrobe', 100, 800, 1),
(2, 'Dining table', 1000, 560, 3),
(3, 'Towel holder', 10000, 25.50, 4),
(4, 'Computer desk', 350, 320.50, 2),
(5, 'Chair', 3000, 210.64, 4),
(6, 'Single bed', 750, 460, 1);
By now, I have to display a sum based on categories repetition, like this:
I am trying it, but I've been facing errors such as aggregate function not allowed in where:
ALTER TABLE
categories
ADD COLUMN
sum INT;
SELECT
categories.name, categories.sum
FROM
categories
JOIN
products
ON categories.id = id_categories
WHERE
categories.sum = products.amount * COUNT(categories.name);
You have to add group by categories.name column to your query.
SELECT
categories.name, sum(products.amount) AS category_sum
FROM
categories
JOIN
products
ON categories.id = id_categories
GROUP BY
categories.name
Returns:
name
category_sum
luxury
350
modern
13000
vintage
1000
wood
850

SQL to assign covid patients to hospitals

I have 2 tables:
CREATE TABLE remdesivir_inventory
(
hospital_id int,
stock int,
state varchar(2)
);
CREATE TABLE remdesivir_requests
(
patient_id int,
prescribed_qty int,
state varchar(2)
);
I want to write a SQL that inserts rows in the remdesivir_assignments table
Every patient whose request can be fulfilled (until the stock runs out) will have a representative row in
the remdesivir_assignments table.
Each patient can be assigned to only 1 hospital (ie. requests cannot be split)
The 'state' of the patient and the hospital must match
CREATE TABLE remdesivir_assignments
(
patient_id int,
hospital_id int
);
Example:
INSERT INTO remdesivir_inventory VALUES (1, 200, 'CA');
INSERT INTO remdesivir_inventory VALUES (2, 100, 'FL');
INSERT INTO remdesivir_inventory VALUES (3, 500, 'TX');
INSERT INTO remdesivir_requests VALUES (10, 100, 'CA');
INSERT INTO remdesivir_requests VALUES (20, 200, 'FL');
INSERT INTO remdesivir_requests VALUES (30, 300, 'TX');
INSERT INTO remdesivir_requests VALUES (40, 100, 'AL');
INSERT INTO remdesivir_requests VALUES (50, 200, 'CA');
In this scenario, the following rows will be inserted to the remdesivir_assignments table
(10, 1)
(30, 3)
You can use a cumulative sum and join:
select rr.*, ri.hospital_id
from (select rr.*,
sum(prescribed_qty) over (partition by state order by patient_id) as running_pq
from remdesivir_requests rr
) rr join
remdesivir_inventory ri
on ri.state = rr.state and
rr.running_pq <= ri.stock
Here is a db<>fiddle.

Joining two Hierarchical queries to form larger Hierarchy

I have researched this and know I'm not the first to ask but I can't seem to get my head around it. I have created a simple example that I think will help me crack it if someone can provide the missing link!
I have a table of areas that contains continents and countries in a hierarchy.
I also have a table of places that contains cities and landmarks in a hierarchy. This table contains an area id column to join to the areas table.
create table areas
(
id NUMBER not null,
name VARCHAR2(200) not null,
parent_id NUMBER
);
-- Top Level
Insert into areas (id, name)
Values (1, 'Europe');
Insert into areas (id, name)
Values (2, 'Americas');
Insert into areas (id, name)
Values (3, 'Asia ex Japan');
Insert into areas (id, name)
Values (4, 'Japan');
-- Jurisdictions
Insert into areas (id, name, parent_id)
Values (5, 'UK', 1);
Insert into areas (id, name, parent_id)
Values (7, 'France', 1);
Insert into areas (id, name, parent_id)
Values (6, 'Germany', 1);
Insert into areas (id, name, parent_id)
Values (8, 'Italy', 1);
Insert into areas (id, name, parent_id)
Values (9, 'US', 2);
Insert into areas (id, name, parent_id)
Values (10, 'Australia', 3);
Insert into areas (id, name, parent_id)
Values (11, 'New Zealand', 3);
create table places
(
id NUMBER not null,
name VARCHAR2(200) not null,
area_id NUMBER,
parent_id NUMBER
);
Insert into places (id, name, area_id, parent_id)
Values (1, 'London', 5, NULL);
Insert into places (id, name, area_id, parent_id)
Values (2, 'Bath', 5, NULL);
Insert into places (id, name, area_id, parent_id)
Values (3, 'Liverpool', 5, NULL);
Insert into places (id, name, area_id, parent_id)
Values (4, 'Paris', 7, NULL);
Insert into places (id, name, area_id, parent_id)
Values (5, 'New York', 9, NULL);
Insert into places (id, name, area_id, parent_id)
Values (6, 'Chicago', 9, NULL);
Insert into places (id, name, area_id, parent_id)
Values (7, 'Kings Cross', 5, 1);
Insert into places (id, name, area_id, parent_id)
Values (8, 'Tower of London', 5, 1);
I can query these tables independently like this:
SELECT a.*, level FROM areas a
start with parent_id is null
connect by prior id = parent_id
SELECT p.*, level FROM places p
start with parent_id is null
connect by prior id = parent_id
Is someone able to show me the last step to join these into one query with four levels? I've been working with Oracle for years but somehow this never came up!
If there was no connect by prior in the places table, just a list of cities with an area id, would this be easier?
Thank you
Is it what you need?
with src as (
select 'A' type, a.id, a.name, a.parent_id, null area_id from areas a
union all
select 'P', -p.id id, p.name, -p.parent_id parent_id, area_id from places p)
select
src.*, level
from
src
start with
type = 'A' and parent_id is null
connect by
parent_id = prior id or
parent_id is null and area_id = prior id

Display 2 columns for each header

In SQL Server 2008 I have a table People (Id, Gender, Name).
Gender is either Male or Female. There can be many people with the same name.
I would like to write a query that displays for each gender the top 2 names
by count and their count, like this:
Male Female
Adam 23 Rose 34
Max 20 Jenny 15
I think that PIVOT might be used but all the examples I have seen display only one column for each header.
Here is an example on SQL Fiddle -- http://sqlfiddle.com/#!3/b3477/1
This uses an couple of common table expressions to separate the genders.
create table People
(
Id int,
Gender varchar(50),
Name varchar(50)
)
;
insert into People values (1, 'Male', 'Bob');
insert into People values (2, 'Male', 'Bob');
insert into People values (3, 'Male', 'Bill');
insert into People values (4, 'Male', 'Chuck');
insert into People values (5, 'Female', 'Anne');
insert into People values (6, 'Female', 'Anne');
insert into People values (7, 'Female', 'Bobbi');
insert into People values (8, 'Female', 'Jane');
with cteMale as
(
select Name as 'MaleName', Count(*) as Num, ROW_NUMBER() over(order by count(*) desc, Name) RowNum
from People
where Gender = 'Male'
group by Name
)
,
cteFemale as
(
select top 2 Name as 'FemaleName', Count(*) as Num, ROW_NUMBER() over(order by count(*) desc, Name) RowNum
from People
where Gender = 'Female'
group by Name
)
select a.MaleName, a.Num as MaleNum, b.femaleName, b.Num as FemaleNum
from cteMale a
join cteFemale b on
a.RowNum = b.RowNum
where a.RowNum <= 2
Use a windowing function. Below is a complete solution using a temporary table #people.
-- use temp db
use tempdb;
go
-- drop test table
--drop table #people;
--go
-- create test table
create table #people (my_id int, my_gender char(1), my_name varchar(25));
go
-- clear test table
delete from #people;
-- three count
insert into #people values
(23, 'M', 'Adam'),
(34, 'F', 'Rose');
go 3
-- two count
insert into #people values
(20, 'M', 'Max'),
(15, 'F', 'Jenny');
go 2
-- one count
insert into #people values
(20, 'M', 'John'),
(15, 'F', 'Julie');
go
-- grab top two by gender
;
with cte_Get_Top_Two as
(
select ROW_NUMBER() OVER(PARTITION BY my_gender ORDER BY count() DESC) AS my_window,
my_gender, my_name, count() as total
from #people
group by my_gender, my_name
)
select * from cte_Get_Top_Two where my_window in (1, 2)
go
Here is the output.
PS: You can drop my_id from the table since it does not relate to your problem but does not change solution.

How can I insert 100000 rows in SQL Server?

INSERT INTO pantscolor_t (procode, color, pic)
VALUES
('74251', 'Black', '511black.jpg'),
('74251', 'OD Green', '511odgreen.jpg'),
('74251', 'Black', '511black.jpg'),
('74251', 'OD Green', '511odgreen.jpg'),
('74251', 'Black', '511black.jpg'),
('74251', 'OD Green', '511odgreen.jpg'),
..........
..........
..........
INSERT INTO pantscolor_t (procode,color,pic)
VALUES
('74251', 'Charcoal', '511charcoal.jpg'),
('74251', 'Charcoal', '511charcoal.jpg'),
('74251', 'Charcoal', '511charcoal.jpg'),
('74251', 'Charcoal', '511charcoal.jpg'),
.............
.............
.............
INSERT INTO........................
INSERT INTO........................
INSERT INTO........................
INSERT INTO........................
I have 100000 rows like this but my insert statements bigger than 1000 rows. When I run the SQL statement in SSMS, I get an error:
The number of row value expressions in the INSERT statement exceeds the maximum allowed number of 1000 row values.
Another solution is to use a select query with unions.
INSERT INTO pantscolor_t (procode,color,pic)
SELECT '74251', 'Black', '511black.jpg'
UNION ALL SELECT '74251', 'OD Green', '511odgreen.jpg'
UNION ALL SELECT '74251', 'Black', '511black.jpg'
UNION ALL SELECT '74251', 'OD Green', '511odgreen.jpg'
UNION ALL SELECT '74251', 'Black', '511black.jpg'
UNION ALL SELECT '74251', 'OD Green', '511odgreen.jpg'
--etc....
UNION ALL is used instead of UNION in order to speed up the query when dealing with thousands of records. UNION ALL allows for duplicate rows whereas UNION will ensure that duplicates do not exist in the result set. For this scenario we don't want to remove any possible duplicates, so UNION ALL is used.
Create csv file (or some file with defined field delimiter and row delimiter) and use "BULK INSERT" option to load file to database. File can have 100000 rows; there won't be any problem of loading huge file using bulk upload.
http://msdn.microsoft.com/en-us/library/ms188365.aspx
INSERT mytable (col1, col2, col3, col4, col5, col6)
SELECT * FROM (VALUES
('1502577', '0', '114', 'chodba', 'Praha', 'Praha 1'),
('1503483', '0', 'TVP', 'chodba', 'Praha', 'Praha 2'),
/* ... more than 1000 rows ... */
('1608107', '0', '8', 'sklad', 'Tlumačov', 'Tlumačov'),
('1608107', '0', '9', 'sklad', 'Tlumačov', 'Tlumačov')
) AS temp (col1, col2, col3, col4, col5, col6);
By applying the following you should not have any error :
INSERT INTO pantscolor_t (procode,color,pic) VALUES ('74251','Black','511black.jpg')
INSERT INTO pantscolor_t (procode,color,pic) VALUES ('74251', 'OD Green', '511odgreen.jpg')
INSERT INTO pantscolor_t (procode,color,pic) VALUES ('74251', 'Black', '511black.jpg')
INSERT INTO pantscolor_t (procode,color,pic) VALUES ('74251', 'OD Green', '511odgreen.jpg')
INSERT INTO pantscolor_t (procode,color,pic) VALUES ('74251', 'Black', '511black.jpg')
...........
I tried it and it worked, of course you can use the excel to concatenate the values easily.
Create a csv out.csv etc
Then use:
BULK INSERT
FROM 'C:\out.csv'
WITH
(
FIELDTERMINATOR = ',',
ROWTERMINATOR = '\n'
)
GO