Group and sum on multiple ids - sql

My brain has locked up on the following problem.
This for an Bi Publisher report
Users can select one or several employee names from a multi select dropdown
e.g John, Peter, Ann ...
In table Emp we have:
emp_id | emp_name
---
1 | John
2 | Peter
3 | Ann
Table worklimit
emp_id | limit | from_date | to_date
---
1 | 35 | 04-jul-2016 | 08-jul-2016
1 | 15 | 11-jul-2016 | 15-jul-2016
2 | 40 | 04-jul-2016 | 08-jul-2016
2 | 20 | 01-aug-2016 | 05-aug-2016
3 | 27 | 04-jul-2016 | 08-jul-2016
So the result I want is a total sum(limit) for the selected employee
e.g John + Ann 77
Or John + Ann + Peter = 137
This is just the first step. I need to sum up som values from a third table too, and I need to select a date range, and it must be grouped by week or month to make a barchart .
Can some of you clever brains out there point me in the right directions?

Try this:
SELECT SUM(A.limit) FROM
(
SELECT SUM(limit) limit,emp_name
FROM worklimit
JOIN Emp ON Emp.emp_id = worklimit.emp_id
GROUP BY emp_name
) A
WHERE emp_name IN ('John','Peter','Ann')

Related

SQL SELECT most recently created row WHERE something is true

I am trying to SELECT the most recently created row, WHERE the ID field in the row is a certain number, so I don't want the most recently created row in the WHOLE table, but the most recently created one WHERE the ID field is a specific number.
My Table:
Table:
| name | value | num |SecondName| Date |
| James | HEX124 | 1 | Carl | 11022020 |
| Jack | JEU836 | 4 | Smith | 19042020 |
| Mandy | GER234 | 33 | Jones | 09042020 |
| Mandy | HER575 | 7 | Jones | 10052020 |
| Jack | JEU836 | 4 | Smith | 14022020 |
| Ryan | GER631 | 33 | Jacque | 12042020 |
| Sarah | HER575 | 7 | Barlow | 01022019 |
| Jack | JEU836 | 4 | Smith | 14042020 |
| Ryan | HUH233 | 33 | Jacque | 15042020 |
| Sarah | HER575 | 7 | Barlow | 02022019 |
My SQL:
SELECT name, value, num, SecondName, Date
FROM MyTable
INNER JOIN (SELECT NAME, MAX(DATE) AS MaxTime FROM MyTable GROUP BY NAME) grouped ON grouped.NAME = NAME
WHERE NUM = 33
AND grouped.MaxTime = Date
What I'm doing here, is selecting the table, and creating an INNER JOIN where I'm taking the MAX Date value (the biggest/newest value), and grouping by the Name, so this will return the newest created row, for each person (Name), WHERE the NUM field is equal to 33.
Results:
| Ryan | HUH233 | 33 | Jacque | 15042020 |
As you can see, it is returning one row, as there are 3 rows with the NUM value of 33, two of which are with the Name 'Ryan', so it is grouping by the Name, and returning the latest entry for Ryan (This works fine).
But, Mandy is missing, as you can see in my first table, she has two entries, one under the NUM value of 33, and the other with the NUM value of 7. Because the entry with the NUM value of 7 was created most recently, my query where I say 'grouped.MaxTime = Date' is taking that row, and it is not being displayed, as the NUM value is not 33.
What I want to do, is read every row WHERE the NUM field is 33, THEN select the Maximum Time inside of the rows with the value of 33.
I believe what it is doing, prioritising the Maximum Date value first, then filtering the selected fields with the NUM value of 33.
Desired Results:
| Ryan | HUH233 | 33 | Jacque | 15042020 |
| Mandy | GER234 | 33 | Jones | 09042020 |
Any help would be appreciated, thank you.
If I folow you correctly, you can filter with a subquery:
select t.*
from mytable t
where t.num = 33 and t.date = (
select max(t1.date) from mytable t1 where t1.name = t.name and t1.num = t.num
)
Look at your subquery. You want the maximum dates for num 33, but you are selecting the maximum dates independent from num.
I think you want:
select *
from mytable
where (name, date) in
(
select name, max(date)
from mytable
where num = 33
group by name
);

Oracle: apply condition over aggregated functions and not for all rows of the SQL statement

I want to calculate for an Oracle table the max value from a select statement based not on all rows returned from the query, but on a condition over a sub-set of those rows.
I'll try to explain better with an example:
ID | NAME | AGE | COMPANY
1 | Alex | 28 | A
2 | Alan | 22 | A
3 | Bob | 21 | B
4 | Carl | 20 | C
5 | Dave | 24 | C
6 | Eric | 26 | C
7 | Matt | 33 | D
I want to obtain the max age for every company under-25 years, but I also want to count the total numbers of persons for every company.
So, I want this result:
COMPANY | DEPENDENTS | MAX_UNDER_25
A | 2 | 22
B | 1 | 21
C | 3 | 24
D | 1 | (null)
How can I obtain this result with a single SQL query, without joining the elaboration for the sum of records and the other elaboration for the max with condition ?
I want to avoid this select:
SELECT R1.COMPANY, R1.DEPENDENTS, R2.MAX_UNDER_25 FROM
(SELECT COMPANY, COUNT(*) DEPENDENTS FROM TABLE
GROUP BY COMPANY) R1
LEFT JOIN
(SELECT COMPANY, MAX(AGE) MAX_UNDER_25 FROM TABLE
WHERE AGE < 25
GROUP BY COMPANY) R2
ON R1.COMPANY = R2.COMPANY;
It is possible to obtain that with a more simple query?
Simply use a case expression when aggregating MAX:
select company, count(*), max(case when age < 25 then age end)
from table
group by company

In PostgreSQL, how to combine a column from one table with a random value from a second table?

I am building a test dataset that associates various people with families, and all individual people have a unique ID, and each family (which has multiple people) has a unique ID.
I have created 2 tables, each with people in them that have unique "peopleID" values. The first table has unique "familyID" values. So:
The first table ("people1") looks like this:
person_ID | family_ID | first_name | last_name | date_of_birth | address
1 | 1 | John | Smith | 01-01-1901 | 123 Anywhere St
2 | 2 | Jane | Jones | 03-01-1982 | 312 Anyplace Ave
6 | 3 | Harold | Viking | 06-30-1973 | 283 Northman Rd
And the second table (people2) looks like this:
person_ID | family_ID | first_name | last_name | date_of_birth | address
3 | NULL | Richard | Hawkins | 04-20-2003 | NULL
4 | NULL | Juliet | Jordan | 03-02-2005 | NULL
And I want to add the rows from people2 to people1, each with a RANDOM familyID from people1, so the end result looks something like this:
person_ID | family_ID | first_name | last_name | date_of_birth | address
1 | 1 | John | Smith | 01-01-1901 | 123 Anywhere St
2 | 2 | Jane | Jones | 03-01-1982 | 312 Anyplace Ave
6 | 3 | Harold | Viking | 06-30-1973 | 283 Northman Rd
3 | 2 | Richard | Hawkins | 04-20-2003 | NULL
4 | 3 | Juliet | Jordan | 03-02-2005 | NULL
One way I thought of to do this would be to build a reference table from the second table, like so:
SELECT person_ID,
(select family_ID from people1 ORDER BY RANDOM() LIMIT 1) as family_ID
from people2;
But that returns a single random entry from "people1" for every row in "people2." How could I force it to return a DIFFERENT random value for every row? A cursor that iterates through each row in "people2" and selects a different familyID value from "people1," perhaps? How would that be written?
Create a temporary table which will associate a subsequent number to every family_id:
create temporary table family_id_no( no serial primary key, family_id int);
insert into family_id_no(family_id)
select distinct family_id from people1;
Associate random family_id by choosing random no:
select person_id, family_id_no.family_id,
first_name, last_name, date_of_birth, address
from people2
left join family_id_no
using no=trunc(random()*(select max(no) from family_id_no))+1;
Untested
SQL Server formula for generating any random integer in a range is:
SELECT FLOOR(RAND()*(b-a)+a)
where a is the smallest and b is the largest number in the range.
(However, I believe RANDOM() might be the function for Postgresql, not RAND())
So if your people1.family_ID column is an INT column and there are no missing values in the range (the example you gave works since it has 1,2,3), you could try replacing a with MIN(family_ID) and b with MAX(family_ID)
http://www.techonthenet.com/sql_server/functions/rand.php

Make one-to-many relationship look like one-to-one

I'm using postgres 9.3.5.
Given the following data:
select * from department;
id | name
----+-----------
1 | sales
2 | marketing
3 | HR
and
select * from people;
id | department_id | first_name | last_name
----+---------------+------------+-----------
1 | 1 | Tom | Jones
2 | 1 | Bill | Cosby
3 | 2 | Jessica | Biel
4 | 1 | Rachel | Hunter
5 | 2 | John | Barnes
I'd like to return a result set like this:
id | name | first_name-1 | last_name-1 | first_name-2 | last_name-2 | first_name-3 | last_name-3
----+-----------+--------------+-------------+--------------+-------------+--------------+------------
1 | sales | Tom | Jones | Bill | Cosby | Rachel | Hunter
2 | marketing | Jessica | Biel | John | Barnes
3 | HR |
Is this possible?
The answer provided here by Max Shawabkeh using the GROUP_CONCAT is close - but its not returning as extra fields in the dataset, its concatenating them into a single field.
You need cross-tabulation (sometimes called pivot).
Could look like this in your case:
SELECT * FROM crosstab(
$$SELECT d.id, d.name,'p' AS dummy_cat
,concat_ws(' ', p.first_name, p.last_name) AS person
FROM department d
LEFT JOIN people p ON p.department_id = d.id
ORDER BY d.department_id, p.id$$
)
AS ct (id int, department text, person_1 text, person_2 text, person_3 text);
Returns:
id department person_1 person_2 person_3
--------------------------------------------------------
1 sales Tom Jones Bill Cosby Rachel Hunter
2 marketing Jessica Biel John Barnes <NULL>
3 HR <NULL> <NULL> <NULL>
Very similar to this related case (explanation for special difficulties there):
Postgres - Transpose Rows to Columns
But this case is simpler: since you do not seem to care about the order in which persons are listed, you can use the basic one-parameter form of crosstab().
Also, according to your comment, you want all departments, even if no people are assigned. Adjusted the LEFT JOIN accordingly.
Basic details in this related answer:
PostgreSQL Crosstab Query

Using Group By on a non numeric field

If I have a table like this:
ID | Name | Age
1 | Bill | 30
2 | Jim | 20
3 | Bill | 30
4 | Bill | 30
5 | Bob | 25
I want to return this:
ID | Name | Age
1 | Bill | 30
2 | Jim | 20
5 | Bob | 25
I tried this but it doesn't work:
SELECT ID,Max(Name),Age FROM TABLE
GROUP BY ID,Age
What do I got to do?
This should work:
select MIN(ID), NAME, AGE from TABLE group by NAME, AGE
Grouping by ID has no sense, because ID is already unique.
SELECT Max(Name), AGE
FROM TABLE
GROUP BY Age