Complex IF statement with 3 tables - mysql

This is a continuation of this question from yesterday.
Here are my three tables:
Fighters Table
fighter_id | name
-----------------------
1 | John
2 | Steve
3 | Bill
4 | Bobby
Events Table
event_id | event_name | event_date
-------------------------------------------
1 | MMA | 01/01/2010
2 | Cool | 02/20/2010
3 | Yeaa! | 04/15/2010
Fights Table
fight_id | fighter_a | fighter_b | winner | method | event
-----------------------------------------------------------------------
1 | 1 | 2 | 1 | Sub | 1
2 | 4 | 1 | 4 | KO | 2
3 | 1 | 3 | NULL | Draw | 3
Result trying to get
result | opponent | method | event | date
----------------------------------------------------------
Draw | Bill | Draw | Yeaa! | 04/15/2010
Loss | Bobby | KO | Cool | 02/20/2010
Win | Steve | Sub | MMA | 01/01/2010
So in the fights table, fighter_a, fighter_b, and winner are integers that correspond to fighter_id in the Fighters table.
I'm basically on a page retrieving data based on fighter_id ($fighter_id).
I'm trying to create rows with each fight of that fighter that includes his opponent's name, result (win, loss, draw, or nc), method, event_name, and event_date. The challenge is that a winner can be in either fighter_a or fighter_b. It's not always in the same column. I appreciate any help I can get.
select
fight_id,
CASE
WHEN winner is not null and winner=fighter_id then 'win'
WHEN winner is not null and winner<>fighter_id then 'loss'
WHEN winner is null and method='Draw' then 'draw'
WHEN winner is null and method = 'No Contest' then 'no contest'
ELSE ''
END as match_result,
participant.name 'participant',
opponent.name 'opponent'
FROM fights
INNER JOIN fighters as participant on participant.fighter_id = fights.fighter_a
INNER JOIN fighters as oppoent on opponent.fighter_id = fights.fighter_b
WHERE
fighter_a=$fighter_id OR fighter_b=$fighter_id
ORDER BY
event_date DESC

Use a subselect with conditionals to switch the fighter_id you're looking for to column_a if it's in column_b, that way, it simplifies your operations and joins in the outer query:
SELECT
(
CASE
WHEN a.winner = a.f_a THEN 'Win'
WHEN a.winner = a.f_b THEN 'Loss'
WHEN a.winner IS NULL THEN a.method
END
) AS result,
b.name AS opponent,
a.method AS method,
c.event_name AS event,
c.event_date AS date
FROM
(
SELECT
IF(fighter_b = $fighter_id, fighter_b, fighter_a) AS f_a,
IF(fighter_b = $fighter_id, fighter_a, fighter_b) AS f_b,
winner,
method,
event
FROM
fights
WHERE
$fighter_id IN (fighter_a, fighter_b)
) a
INNER JOIN
fighters b ON a.f_b = b.fighter_id
INNER JOIN
events c ON a.event = c.event_id
ORDER BY
c.event_date DESC
Also, if the winner field is null, then just echo the method field. That way, when you want to add yet another type of method where winner is null to your system, you don't have to keep tacking on more conditional checks to your CASE statement.

You could use a union:
SELECT CASE WHEN winner = participant_id THEN 'Win'
WHEN winner = opponent_id then 'Loss'
ELSE method
END AS result, fighters.name AS opponent, method, event_name, event_date
FROM fights
INNER JOIN fighters ON fighter_id = opponent_id
INNER JOIN events ON event = event_id
WHERE participant_id = $fighter_id
UNION ALL
SELECT CASE WHEN winner = participant_id THEN 'Loss'
WHEN winner = opponent_id then 'Win'
ELSE method
END AS result, fighters.name AS opponent, method, event_name, event_date
FROM fights
INNER JOIN fighters ON fighter_id = participant_id
INNER JOIN events ON event = event_id
WHERE opponent_id = $fighter_id
ORDER BY event_date DESC

You could just modify you CASE statements to match with $fighter_id.
So your CASE statements would be -
select fight_id,
CASE
WHEN winner is not null and winner = f.fighter_id then 'win'
WHEN winner is not null and winner <> f.fighter_id then 'loss'
WHEN winner is null and method='Draw' then 'draw'
WHEN winner is null and method = 'No Contest' then 'no contest'
ELSE ''
END as match_result,
f.name AS participant,
(SELECT o.name FROM fighters o WHERE o.fighter_id = IF(f.fighter_id = fs.fighter_a, fs.fighter_b, fs.fighter_a)) AS opponent
FROM fights fs,
fighters f
WHERE f.fighter_id = $fighter_id
AND (fs.fighter_a = f.fighter_id OR fs.fighter_b = f.fighter_id)

SELECT
CASE
WHEN winner is not null and winner=fighter_id then 'win'
WHEN winner is not null and winner<>fighter_id then 'loss'
WHEN winner is null and method='Draw' then 'draw'
WHEN winner is null and method = 'No Contest' then 'no contest'
ELSE ''
END result,
f2.name opponent,
method,
event_name event,
event_date date
FROM fights f1
INNER JOIN fighters f2
ON f1.fighter_b = f2.fighter_id
INNER JOIN events e
ON e.event_id = f1.event
ORDER BY date DESC

Related

How to get the result in single row when using sub queries in SQL Server 2008?

I am trying to get a table with stage_name and its count in respective loan product. Like in below example stage_name is RCO and there are three loan product, Auto loan, Consumer loan and Credit card. Though I have used the logic and getting the right output, but in the output, I am getting the separate row for each stage_name and loan product case. I want only one row with all the three result. Please look at my code below, actual output and desired output:
SELECT
'RCO',
CASE
WHEN sq2.loan_type = 'Consumer loan'
THEN SUM(ISNULL(sq2.user_count, 0))
END AS Consumer_Loan,
CASE
WHEN sq2.loan_type = 'Auto Loan'
THEN SUM(ISNULL(sq2.user_count, 0))
END AS Auto_Loan,
CASE
WHEN sq2.loan_type = 'Credit Card'
THEN SUM(ISNULL(sq2.user_count, 0))
END AS Credit_Card
FROM
(SELECT
'RC0' AS ws_name, 'Consumer loan' AS loan_type,
COUNT(DISTINCT a.bpm_referenceno) AS user_count,
a.takenby AS user_id
FROM
BM_RLOS_DecisionHistoryForm a
INNER JOIN
(SELECT
m.bpm_referenceno
FROM
BM_RLOS_EXTTABLE m
WHERE
m.loan_type = 'Consumer Loan') sq1 ON a.bpm_referenceno = sq1.bpm_referenceno
WHERE
a.winame='RCO'
GROUP BY
a.takenby
UNION
SELECT 'RC0','Auto loan',
count (DISTINCT a.bpm_referenceno), a.takenby
from
BM_RLOS_DecisionHistoryForm a
INNER JOIN
(SELECT
m.bpm_referenceno
FROM BM_RLOS_EXTTABLE m
WHERE m.loan_type='Auto Loan')sq1
ON a.bpm_referenceno = sq1.bpm_referenceno
WHERE a.winame='RCO'
GROUP BY a.takenby
UNION
SELECT 'RC0','Credit Card',
count (DISTINCT a.bpm_referenceno), a.takenby
from
BM_RLOS_DecisionHistoryForm a
INNER JOIN
(SELECT
m.bpm_referenceno
FROM BM_RLOS_EXTTABLE m
WHERE m.loan_type='Credit Card')sq1
ON a.bpm_referenceno = sq1.bpm_referenceno
WHERE a.winame='RCO'
GROUP BY a.takenby) sq2
GROUP BY sq2.ws_name,sq2.loan_type
Actual output:
|--------------|-------------|-------------|-------------|
| Stg_nm | Cons_ln | Auto_lan | Credit_card |
|--------------|-------------|-------------|-------------|
| RCO | NULL | NULL | 8 |
|--------------|-------------|-------------|-------------|
| RCO | NULL | 55 | NULL |
|--------------|-------------|-------------|-------------|
| RCO | 81 | NULL | NULL |
|--------------|-------------|-------------|-------------|
Required Output
|--------------|-------------|-------------|-------------|
| Stg_nm | Cons_ln | Auto_lan | Credit_card |
|--------------|-------------|-------------|-------------|
| RCO | 81 | 55 | 8 |
|--------------|-------------|-------------|-------------|
The top half should be like this - reverse the usage of SUM and CASE, and remove the last GROUP BY altogether
SELECT
'RCO',
SUM(CASE
WHEN sq2.loan_type = 'Consumer loan'
THEN sq2.user_count
ELSE 0 END
)
AS Consumer_Loan,
SUM(CASE
WHEN sq2.loan_type = 'Auto loan'
THEN sq2.user_count
ELSE 0 END
)
AS Auto_Loan,
SUM(CASE
WHEN sq2.loan_type = 'Credit Card'
THEN sq2.user_count
ELSE 0 END
)
AS Credit_Card
FROM
<your existing query, with the final GROUP BY removed>
But you need to remove the GROUP BY from the end altogether

mySQL Query select / join not working as expected

let me preface that I've done a good bit of searching to see if this issue can be resolved but have yet to find an answer that relates, so I'll ask now. I have a query that works (mostly) to pull from two separate tables to show a total amount of pc models in each of four locations(these locations are in a single column, so I'm trying to select them as their own columns through aliases). The query pulls all required info, but not as I was hoping it would.
Expectation:
model | loc1 | loc2 | loc3 | loc4
5530 | 3 | 2 | 1 | 0
6440 | 10 | 0 | 3 | 20
Reality:
model | loc1 | loc2 | loc3 | loc4
5530 | 3 | null | null | null
5530 | null | 2 | null | null
5530 | null | null | 1 | null
etc..
Here is my query, any help to make the reality match the expectation if possible would be appreciated.
SELECT
a.model,
(select COUNT(a.model)
WHERE a.location = 'AoC-Reno') AS Reno,
(select count(a.model)
where a.location = 'AoC-Fargo') AS Fargo,
(select count(a.model)
where a.location = 'EoC') AS EoC,
(select count(a.model)
where a.location = 'APoC') as APoC
FROM assets AS a
join models m on m.model = a.model
WHERE m.type IN ('Desktop','Laptop')
AND a.model = m.model
AND a.status != 'Recycled'
GROUP BY m.model, a.location
ORDER BY m.model
Use conditional aggregation:
SELECT a.model,
SUM(a.location = 'AoC-Reno') AS Reno,
SUM(a.location = 'AoC-Fargo') AS Fargo,
SUM(a.location = 'EoC') AS EoC,
SUM(a.location = 'APoC') as APoC
FROM models m JOIN
assets a
on m.model = a.model
WHERE m.type IN ('Desktop', 'Laptop') and a.status <> 'Recycled'
GROUP BY m.model
ORDER BY m.model;
Your query has multiple issues:
You are using a "subquery", but there is no FROM clause.
You are aggregating by both model and location, but you want only one row per model.
The JOIN condition is both in the ON clause and repeated in the WHERE clause.
You can do a conditional aggregation (count)
SELECT a.model,
SUM(CASE WHEN a.location = 'AoC-Reno' THEN 1 END) reno,
SUM(CASE WHEN a.location = 'AoC-Fargo' THEN 1 END) farggo,
SUM(CASE WHEN a.location = 'EoC' THEN 1 END) eoc,
SUM(CASE WHEN a.location = 'APoC' THEN 1 END) apoc
FROM assets AS a JOIN models m
ON m.model = a.model
WHERE m.type IN ('Desktop','Laptop')
AND a.status != 'Recycled'
GROUP BY m.model
ORDER BY m.model

MySQL count with group by

Sorry for an unclear question, my english isnt that good. So theres my query:
SELECT ticketID,
status,
COUNT(status) as count,
statusName,
assign
FROM ticket, department, status
WHERE ticket.department = 100
AND ticket.department = department.departID
AND ticket.status = status.statusID
GROUP BY statusName,assign
and this is the result:
| ticketID | count | statusName | assign |
|:----------|-----------:|:---------:|:-------:
| 1002 | 2 | open | NULL |
| 1020 | 1 | open | James |
| 1021 | 1 | open | Nick |
| 1015 | 1 | overdue | NULL |
My goal was to count ticket by their status, and if status='open' and assign = null then the status would change to 'unassigned', i need a better solution or just a way to merge the result where 'James' and 'Nick' to be one, as i only need to know whether the ticket is assigned or not.
Don't see where you need department table, think you can remove it safely, but...
SELECT a.statusID, a.Status, Count(*)
FROM(
SELECT statusID,
(CASE WHEN statusName = 'open' and assign IS NULL THEN 'unassigned'
WHEN statusName ='open' and assign IS NOT NULL THEN 'assigned'
ELSE statusName
END) as Status
FROM ticket
INNER JOIN department ON ticket.department = department.departID
INNER JOIN status ON ticket.status = status.statusID
WHERE ticket.department = 100) as a
GROUP BY a.Status, a.statusID
SqlFiddle (simplified)
SELECT ticketID,
status,
COUNT(status) as count,
statusName,
'unassigned'
FROM ticket, department, status
WHERE ticket.department = 100
AND ticket.department = department.departID
AND ticket.status = status.statusID
AND assign is NULL
GROUP BY statusName
UNION
SELECT ticketID,
status,
COUNT(status) as count,
statusName,
'assigned'
FROM ticket, department, status
WHERE ticket.department = 100
AND ticket.department = department.departID
AND ticket.status = status.statusID
AND assign is NOT NULL
GROUP BY statusName
This query fetches the unassigned and assigned tickets in the form of a union of two disjoint resultsets.
SELECT ticketID,status,sum(CASE status
WHEN 'open' then 1
WHEN '' then 1
WHEN NULL then 1
else 0) as count,statusName,assign
FROM ticket, department, status WHERE ticket.department = 100
AND ticket.department = department.departID
AND ticket.status = status.statusID
GROUP BY statusName,assign

Select data from multiple tables

How can I join multiple rows in just one single row through mysql?
Example :
Student Table
Sno.| Name | Subjects
1. | ABC | 1
2. | ABC | 3
3. | ABC | 4
4. | FMC | 2
5. | ABC | 4
6. | JBC | 4
Papers Table:
Sno. | Paper Name | Type
1. French Optional
2. English Mandatory
3. Japenese Optional
4. Maths Optional
Now I want it in this format
Sno.| Name| Sub1 | Sub2 | Sub3 | Sub4 |
1. | ABC | French | Japenese| Maths | Null |
2. | FMC | Null | Null | Null | Null |
3. | JBC | Maths | Null | Null | Null |
What i want to select is papers name from papers table and student name, four subjects from the other table. I only want to see those paper which are optional. I am not sure what to do?
I think the concept you are looking for is pivoting.
Version with join
select
name,
max(if(s.subject = 1 and p.type = 'Optional', p.paper_name, null)) as subject1,
max(if(s.subject = 2 and p.type = 'Optional', p.paper_name, null)) as subject2,
max(if(s.subject = 3 and p.type = 'Optional', p.paper_name, null)) as subject3,
max(if(s.subject = 4 and p.type = 'Optional', p.paper_name, null)) as subject4
from
students s
inner join
papers p on p.sno = s.subject
group by s.name
SQL Fiddle Demo
Version with subselects instead of a join
select
name,
max(
case
when subject = 1
then (select paper_name from papers p where p.sno = subject and type = 'Optional')
else
null
end) as subject1,
max(
case
when subject = 2
then (select paper_name from papers p where p.sno = subject and type = 'Optional')
else
null
end) as subject2,
max(
case
when subject = 3
then (select paper_name from papers p where p.sno = subject and type = 'Optional')
else
null
end) as subject3,
max(
case
when subject = 4
then (select paper_name from papers p where p.sno = subject and type = 'Optional')
else
null
end) as subject4
from
students
group by
name
SQL Fiddle Demo

Conditional Query for Employee Training Analysis Query

I need to run training queries that return results for questions like the following: "Who has completed this training, but not that training?"
In the simplified table below, I would like to know which employee has completed training_id 1 (as indicated by a date in the completed_date field), but has not finished training_id 7.
+-------------+-------------+----------------+
| emp_id | training_id | completed_date |
+-------------+-------------+----------------+
| 1 | 1 | 2010-04-02 |
+-------------+-------------+----------------+
| 1 | 7 | Null |
+-------------+-------------+----------------+
| 2 | 1 | Null |
+-------------+-------------+----------------+
| 2 | 7 | Null |
+-------------+-------------+----------------+
Desired result would be emp_id 1 and we'd like to return his/her completed training and non-completed training per the query parameters:
+-------------+-------------+----------------+
| emp_id | training_id | completed_date |
+-------------+-------------+----------------+
| 1 | 1 | 2010-04-02 |
+-------------+-------------+----------------+
| 1 | 7 | Null |
+-------------+-------------+----------------+
I can't figure out how to do this with a regular query because it seems to need IF logic. Ex: Return rows where this one training is complete and return rows where this second training is not complete BUT ONLY if the first training is complete.
How do I express something like that in SQL?
You can use an EXISTS clause
SELECT t.*
FROM training t
# which employee has completed training_id 1
WHERE t.training_id = 1 and t.completed_date is not null
#but has not finished training_id 7.
AND NOT EXISTS (
SELECT * FROM training t2
where t2.emp_id=t.emp_id
and t2.training_id = 7
and t2.completed_date is not null)
If you wanted to test something more complex, like completed (4,5,6) but not (1,9), then you can use counts:
SELECT t.emp_id
FROM training t
WHERE t.training_id in (4,5,6) and t.completed_date is not null
group by t.emp_id
having count(distinct emp_id) = 3
AND NOT EXISTS (
SELECT * FROM training t2
where t2.emp_id=t.emp_id
and t2.training_id in (1,9)
and t2.completed_date is not null)
And finally if you need the full employee training record
SELECT e.*
FROM
(
SELECT t.emp_id
FROM training t
WHERE t.training_id in (4,5,6) and t.completed_date is not null
group by t.emp_id
having count(distinct emp_id) = 3
AND NOT EXISTS (
SELECT * FROM training t2
where t2.emp_id=t.emp_id
and t2.training_id in (1,9)
and t2.completed_date is not null)
) search
inner join training e on e.emp_id = search.emp_id
order by e.emp_id
select * from training where emp_id in
(
select distinct emp_id from training where completed_Date is not null
)
You could do a self-join on this table ("pretend it is two identical tables and join them"):
SELECT t1.emp_id, t1.training_id, t1.completed_date,
t2.training_id, t2.completed_date
FROM training AS t1 /* the aliases are needed for the self-join */
JOIN training AS t2
ON t1.emp_id = t2.emp_id
AND t2.training_id = 7 /* second training ID */
WHERE t1.training_id = 1 /* first training ID */
That should give you a result like this:
t1.emp_id | t1.training_id | t1.completed_date | t2.training_id | t2.completed_date
1 1 2010-04-02 7 NULL
2 1 NULL 7 NULL
You can then further limit the WHERE query, e.g. with
AND t1.completed_date IS NOT NULL
AND t2.completed_date IS NULL
which will give you the set that you want - training 1 completed, training 7 not.