Use max of column and if null use min - sql

I have a table with 10 milestones in the column milestone. The column milestone_achieved has either the value OK or NULL.
The name column has just names, whenever someone new enters, all the milestones are entered in the database with NULL.
Here is what a typical table looks like:
+------+-----------+--------------------+
| name | milestone | milestone_achieved |
+------+-----------+--------------------+
| John | 1 | OK |
| John | 2 | OK |
| John | 3 | NULL |
| John | 4 | NULL |
| John | 5 | NULL |
| John | 6 | NULL |
| Mary | 1 | OK |
| Mary | 2 | OK |
| Mary | 3 | OK |
| Mary | 4 | OK |
| Mary | 5 | OK |
| Mary | 6 | OK |
| Tim | 1 | NULL |
| Tim | 2 | NULL |
| Tim | 3 | NULL |
| Tim | 4 | NULL |
| Tim | 5 | NULL |
| Tim | 6 | NULL |
+------+-----------+--------------------+
Now I want the SQL query to return:
+------+-----------+--------------------+
| name | milestone | milestone_achieved |
+------+-----------+--------------------+
| John | 2 | OK |
| Mary | 6 | OK |
| Tim | 1 | NULL |
+------+-----------+--------------------+
My query right now looks like this:
SELECT name, MAX(milestone) FROM table HAVING milestone_achieved = 'OK' GROUP BY name
UNION ALL
SELECT name, MIN(milestone) FROM table HAVING milestone_achieved IS NULL AND MIN(milestone) = 1 GROUP BY name
This works in 90% of the cases, the problem occurs when e.g. milestone 1 and 2 was completed, but then milestone 1 was "uncompleted" because it didn't fit the specific criteria or whatever (imagine an assembly line where cars are assembled and a screw =milestone 1 isn't tight enough but the paint =milestone 2 is already on it or whatever else you can imagine, I have terrible imagination).
I am now looking fo a way to properly display those 10% cases.

One method is:
SELECT name, MAX(milestone)
FROM table
WHERE milestone_achieved = 'OK'
GROUP BY name
UNION ALL
SELECT name, MIN(milestone)
FROM table
GROUP BY name
HAVING MIN(milestone_achieved) IS NULL;
This follows the structure of your logic. You can do this with one SELECT:
SELECT name,
COALESCE(MAX(CASE WHEN milestone_achieved = 'OK' THEN milestone END),
MIN(milestone)
)
FROM table
GROUP BY name

Related

How to select 2 data from 1 column based on other relations in other table

Say, I have a table like the following called "name":
| nid | name |
----------------
| 1 | john |
| 2 | mike |
| 3 | tom |
| 4 | jack |
| 5 | will |
| 6 | david | ...
and another table like the following called "relation_father_son":
| rid | fnid | snid |
---------------------
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 4 | 5 |
| 4 | 2 | 6 | ...
then I would like a result like the following:
| father | son |
------------------
| john | mike |
| john | tom |
| jack | will |
| mike | david | ...
What the should the SQL query be?
The query would be:
SELECT
f.name AS father,
s.name AS son
FROM relation_father_son
INNER JOIN name AS f
ON (nid = fnid)
INNER JOIN name AS s
ON (nid = snid)
First of all, it is confusing that the first table is named as name. You should rename it to a more distinguished name, such as family_names. Read more at: Is name a reserved word in MySQL?
For the desired result, you can use the following query:
SELECT
(SELECT `name` FROM `family_names` WHERE nid=fnid) AS father,
(SELECT `name` FROM `family_names` WHERE nid=snid) AS son
FROM relation_father_son

SQL Get cases related to a user and the number of files attached to that case

Hi everyone got a little stuck on an sql query. I have four tables
users
+----+------------+-----------+--------+
| id | first_name | last_name | active |
+----+------------+-----------+--------+
| 1 | Joe | Bloggs | 1 |
| 2 | John | Doe | 1 |
| 3 | Dave | Smith | 1 |
+----+------------+-----------+--------+
cases
+----+-----------+-------------+
| id | case_code | case_name |
+----+-----------+-------------+
| 1 | THEC12C | Test Case 1 |
| 2 | ABCD23A | Test Case 2 |
+----+-----------+-------------+
case_creditors
+----+---------+-------------+
| id | case_id | creditor_id |
+----+---------+-------------+
| 1 | 1 | 3 |
| 2 | 2 | 1 |
+----+---------+-------------+
case_files
+----+---------+----------+-----------+
| id | case_id | filename | file type |
+----+---------+----------+-----------+
| 1 | 1 | test.pdf | pfd |
| 2 | 2 | file.txt | txt |
| 3 | 2 | word.doc | doc |
+----+---------+----------+-----------+
When a user logs in i need to show a table with the users accociated cases the number of files attached to that case so if Joe Blogs loged in head see the following table
+-----------+-------------+-------+
| Case Code | Case Name | Files |
+-----------+-------------+-------+
| ABCD23A | Test Case 2 | 2 |
+-----------+-------------+-------+
ive been trying to write the sql statement to do this but am getting stuck on the query and wandered if someone could help give me some pointers. the sql ive gor so far
SELECT * FROM cases
(SELECT COUNT(*) FROM case_files WHERE case_files.case_id = cases.id) as Files
JOIN case_creditors ON cases.id = case_creditors.case_id
WHERE case_creditors.creditor_id = 1
managed to sort this with
SELECT
ips_case.*,
COUNT(case_files.file_id) AS Files
FROM
ips_case
LEFT JOIN case_files ON ips_case.id = case_files.case_id
JOIN case_creditors ON ips_case.id = case_creditors.case_id
WHERE
case_creditors.creditors_id = 4
GROUP BY
ips_case.id

SQL compare multiple rows or partitions to find matches

The database I'm working on is DB2 and I have a problem similar to the following scenario:
Table Structure
-------------------------------
| Teacher Seating Arrangement |
-------------------------------
| PK | seat_argmt_id |
| | teacher_id |
-------------------------------
-----------------------------
| Seating Arrangement |
-----------------------------
|PK FK | seat_argmt_id |
|PK | Row_num |
|PK | seat_num |
|PK | child_name |
-----------------------------
Table Data
------------------------------
| Teacher Seating Arrangement|
------------------------------
| seat_argmt_id | teacher_id |
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
------------------------------
---------------------------------------------------
| Seating Arrangement |
---------------------------------------------------
| seat_argmt_id | row_num | seat_num | child_name |
| 1 | 1 | 1 | Abe |
| 1 | 1 | 2 | Bob |
| 1 | 1 | 3 | Cat |
| | | | |
| 2 | 1 | 1 | Abe |
| 2 | 1 | 2 | Bob |
| 2 | 1 | 3 | Cat |
| | | | |
| 3 | 1 | 1 | Abe |
| 3 | 1 | 2 | Cat |
| 3 | 1 | 3 | Bob |
| | | | |
| 4 | 1 | 1 | Abe |
| 4 | 1 | 2 | Bob |
| 4 | 1 | 3 | Cat |
| 4 | 2 | 2 | Dan |
---------------------------------------------------
I want to see where there are duplicate seating arrangements for a teacher. And by duplicates I mean where the row_num, seat_num, and child_name are the same among different seat_argmt_id for one teacher_id. So with the data provided above, only seat id 1 and 2 are what I would want to pull back, as they are duplicates on everything but the seat id. If all the children on the 2nd table are exact (sans the primary & foreign key, which is seat_argmt_id in this case), I want to see that.
My initial thought was to do a count(*) group by row#, seat#, and child. Everything with a count of > 1 would mean it's a dupe and = 1 would mean it's unique. That logic only works if you are comparing single rows though. I need to compare multiple rows. I cannot figure out a way to do it via SQL. The solution I have involves going outside of SQL and works (probably). I'm just wondering if there is a way to do it in DB2.
Does this do what you want?
select d.teacher_id, sa.row_num, sa.seat_num, sa.child_name
from seatingarrangement sa join
data d
on sa.seat_argmt_id = d.seat_argmt_id
group by d.teacher_id, sa.row_num, sa.seat_num, sa.child_name
having count(*) > 1;
EDIT:
If you want to find two arrangements that are the same:
select sa1.seat_argmt_id, sa2.seat_argmt_id
from seatingarrangement sa1 join
seatingarrangement sa2
on sa1.seat_argmt_id < sa2.seat_argmt_id and
sa1.row_num = sa2.row_num and
sa1.seat_num = sa2.seat_num and
sa1.child_name = sa2.child_name
group by sa1.seat_argmt_id, sa2.seat_argmt_id
having count(*) = (select count(*) from seatingarrangement sa where sa.seat_argmt_id = sa1.seat_argmt_id) and
count(*) = (select count(*) from seatingarrangement sa where sa.seat_argmt_id = sa2.seat_argmt_id);
This finds the matches between two arrangements and then verifies that the counts are correct.

SQL only select rows with max date within each user

SQL beginner here. I've got a simple test that users take, and each row is the answer to one of their questions. They're allowed to take the exam once per day, so some people take it a second time on another day, and thus will have many rows with different test dates. What I'm basically trying to do is get each user's most recent score.
Here is what my data looks like (table name is dumdum):
+----------+----------------+----------+------------------+
| USERNAME | CORRECT_ANSWER | RESPONSE | DATE_TAKEN |
+----------+----------------+----------+------------------+
| matt | 1 | 1 | 3/23/15 1:04:26 |
| matt | 2 | 2 | 3/23/15 1:04:28 |
| matt | 3 | 3 | 3/23/15 1:04:23 |
| david | 1 | 3 | 3/20/15 1:04:25 |
| david | 2 | 2 | 3/20/15 1:04:28 |
| david | 3 | 1 | 3/20/15 1:04:30 |
| david | 1 | 1 | 3/21/15 11:03:14 |
| david | 2 | 3 | 3/21/15 11:03:17 |
| david | 3 | 2 | 3/21/15 11:03:19 |
| chris | 1 | 2 | 3/17/15 12:45:52 |
| chris | 2 | 2 | 3/17/15 12:45:56 |
| chris | 3 | 3 | 3/17/15 12:45:59 |
| peter | 1 | 1 | 3/19/15 2:45:33 |
| peter | 2 | 3 | 3/19/15 2:45:35 |
| peter | 3 | 2 | 3/19/15 2:45:38 |
| peter | 1 | 1 | 3/20/15 12:32:04 |
| peter | 2 | 2 | 3/20/15 12:32:05 |
| peter | 3 | 3 | 3/20/15 12:32:05 |
+----------+----------------+----------+------------------+
and what I'm trying to get in the end...
+----------+------------------+-------+
| USERNAME | MOST_RECENT_TEST | SCORE |
+----------+------------------+-------+
| matt | 3/23/2015 | 100 |
| david | 3/21/2015 | 33 |
| chris | 3/17/2015 | 67 |
| peter | 3/20/2015 | 100 |
+----------+------------------+-------+
I ran into some trouble because I need to go by day, and not by day/time, so I had to do a weird maneuver where I went to character and back to date... This is what I have so far, but I can't figure out how to use only the scores from the most recent test (right now it's factoring in all scores from every test ever taken)...
SELECT username, to_date(substr(max(test_date),1,9),'dd-MON-yy') as most_recent_test, round((sum(case when response=correct_answer then 1 end)/3)*100,0) as score
FROM dumdum group by username
Any help would be appreciated! Thanks!
There are several solutions to this problem this one uses the WITH clause and the RANK function.
It also uses the TRUNC function rather than to_date(substr(
with mxDate as
(SELECT USERNAME,
TRUNC(DATE_TAKEN) as MOST_RECENT_TEST,
CASE WHEN CORRECT_ANSWER = RESPONSE THEN 1 else 0 END as SCORE,
RANK () OVER (PARTITION BY USERNAME
ORDER BY TRUNC(DATE_TAKEN) DESC) Rk
FROM dumdum)
SELECT
USERNAME,
MOST_RECENT_TEST,
SUM(SCORE)/3 * 100
FROM
mxDate
WHERE
rk = 1
GROUP BY
USERNAME,
MOST_RECENT_TEST
Demo

Select all child record but need values of parent or grandparent record

I'm working on a project that uses a MariaDB v5.5 database to keep track of employees in a tree based higherarchy. Each person in this tree can have various 'flags' associated with them. In this case, these flags are stored using a bitmask.
My objects look like the following
Employee Table description
+--------------+-------------+--------------------------------------+
| Name | Field | Description |
+--------------+-------------+--------------------------------------+
| employee_id | INT | Unique key |
| name | VARCHAR(45) | Employees name |
| flags | INT(4) | Bitmask of employee attributes |
| parent_id | INT | the employee_id of the parent record |
+--------------+-------------+--------------------------------------+
'flag' bitmap description
+-----+--------------+
| Bit | Flag |
+-----+--------------+
| 0 | CEO |
| 1 | MANAGER |
| 2 | PROJECT_LEAD |
| 3 | SALES_PERSON |
| 4 | MAINTANCE |
+-----+--------------+
Employee Table Data
+----+--------+------------+---------------------------+
| id | name | parent_id | flags |
+----+--------+------------+---------------------------+
| 1 | Lisa | NULL | CEO |
| 2 | Steve | 1 | MANAGER |
| 3 | Pat | 1 | MANAGER |
| 4 | Mary | 2 | SALES_PERSON,PROJECT_LEAD |
| 5 | Phil | 4 | SALES_PERSON,MAINTANCE |
| 6 | Jim | 3 | SALES_PERSON,MAINTANCE |
| 7 | Anna | 3 | SALES_PERSON,MAINTANCE |
+----+--------+------------+---------------------------+
Let's say I want to query all employees who have the "MAINTANCE" flag BUT, I need to return the id of the parent record that has the "MANAGER" flag. So my result should look like this.
> SELECT id, name, manager_id, manager_name FROM ...
+----+--------+------------+--------------+
| ID | Name | manager_id | manager_name |
+----+--------+------------+--------------+
| 5 | Phil | 2 | Steve |
| 6 | Jim | 3 | Pat |
| 7 | Anna | 3 | Pat |
+----+--------+------------+--------------+
So how do I build my query to give me what I want?
Note, that this tree can be more than just 3 levels deep and the query still needs to work.
Select
a.ID
,a.Name
,a.manager_ID
,b.Manager_Name
From
EmployeeTable As a
Left Join
EmployeeTable As b
On
a.Mangager_ID = b.ID
Where
a.Flag = 'Maintenance'
And
b.Flag = 'Manager'
This should give you what you are looking for.