How to Select same column in 1 table twice in mysql - mysql

How can i select 1 column twice using only 1 table, the problem is i have to separate 1 column into 2 return
ex: table Logs
TIMELOGS 11:00 , 12:12 , 13:00 , 17:01 , 17:05 , 17:10
TIMEMODE 0 , 0 , 0 , 1 , 1 , 1
the output should be
IN 11:00 , 12:12 , 13:00
OUT 17:01, 17:05 , 17:10
How can I combine these to queries
Select TIMELOGS as IN FROM table_logs where TIMEMODE = 0;
and
Select TIMELOGS as OUT FROM table_logs where TIMEMODE = 1;

If I understand correctly, you want to enumerate the 0s and the 1s and then group by that:
select max(case when mode = 0 then time end),
max(case when mode = 1 then time end)
from (select l.*,
(#rn := if(#m = mode, #rn + 1,
if(#m := mode, 1, 1)
)
) as rn
from logs l cross join
(select #rn := 0, #m := -1) params
order by mode, time
) l
group by rn;

To output the format you described, (suppose you're using MySQL) you could do:
select
case when TIMEMODE = 0 then 'IN' else 'OUT' end,
group_concat(TIMELOGS)
from table_logs
group by TIMEMODE

Related

How to combine two select query with different columns

I know that union can be done to combine two queries if they have same number and type of columns. But I've a condition where I've to combine two select statement with different tables and different columns though 1 table is common in both i.e. PatientAppointment. Here are the two statements:
select p.CDRId, p.Gender,p.MRN,p.DoB as DOB,pa.AppointmentDateTime,cn.Description,ehv.ProgramName,
cn.CreatedBy as CareTeamStaffMember,cn.Profile as Role,cn.Title as Credentials,
date_format(pa.AppointmentDateTime, '%Y-%m') BillingMonth,
((case when duration like '% hour%' then substring_index(duration, ' hour', 1) * 60 else 0 end) +
(case when duration like '%min%' then substring_index(substring_index(duration, ' min', 1), ' ', -1) + 0 else 0 end)) as minutes
from Patient p inner join EnrollmentHistoryView ehv on ehv.CDRId = p.CDRId
inner join ClinicalNote cn on cn.CDRId = p.CDRId
inner join PatientAppointment pa on pa.CDRId = p.CDRId
where p.CDRId='9493b505-03b9-46a0-b009-99b34f7a5d41'
and ehv.ProgramName!='N/A'
group by p.CDRId, p.Gender, p.MRN, p.Dob, pa.AppointmentDateTime,cn.Description,cn.CreatedBy,cn.Profile,cn.Title,pa.Duration,ehv.ProgramName
UNION
SELECT AppointmentDateTime,
duration,
minutes,
CASE WHEN #prev_month != BillingMonth
THEN total >= 20
WHEN #prev_total < 20
THEN 1
ELSE 0
END 99457Elig,
CASE WHEN #prev_month != BillingMonth
THEN total >= 40
WHEN #prev_total < 40
THEN 1
ELSE 0
END 99458Elig,
#prev_month := BillingMonth BillingMonth,
#prev_total := total total
FROM (select AppointmentDateTime,
duration,
#cur_dur := ((case when duration like '% hour%' then substring_index(duration, ' hour', 1) * 60 else 0 end) +
(case when duration like '%min%' then substring_index(substring_index(duration, ' min', 1), ' ', -1) + 0 else 0 end)) as minutes,
CASE WHEN #year_month = date_format(AppointmentDateTime, '%Y-%m')
THEN #cum_sum := #cum_sum + #cur_dur
ELSE #cum_sum := #cur_dur
END total,
#year_month := date_format(AppointmentDateTime, '%Y-%m') BillingMonth
from PatientAppointment, (SELECT #year_month:='', #cum_sum:=0, #cur_dur:=0) variables
ORDER BY AppointmentDateTime) subquery,
(SELECT #prev_month:=0, #prev_total:=0) variable
ORDER BY AppointmentDateTime
The query is too complex and I can't create the data set also. Please help me with the approach. Give me some suggestion at least. I'll try it myself.
Consider your first query as Query1 and second query as Query2, you can use simple join between these two.
As you said PatientAppointment table is common in both, use its primary key(CDRId) as joining between these two. So your query would look like.
SELECT *
FROM ( Query1 ) AS table1
INNER JOIN ( Query2) AS table2 ON table1.CDRId = table2.CDRId;

How to create a view that show last 4 quarters data?

My environment is 5.1.72-community.
I have a table named production, like:
id, person_id, num, p_year, p_quarter
1, 1, 43, 2018, 2
2, 1, 92, 2018, 1
3, 1, 108, 2017, 4
4, 2, 41, 2018, 2
...
By the time of '2018-04-14', we know quarter=2, and year=2018. I define quarter_1 is current quarter of current year, and quarter_2 is previous quarter, ans so on. Now I want to create a view like:
person_id, num_quarter_1, num_quarter_2, num_quarter_3, num_quarter_4
1, 43, 92, 108, 0
2, 41, 0, 0, 0
As that, if there no num in that quarter, fill 0.
How to create that view please?
Hmmm. If I interpret "last four quarters" as "the last four quarters that are in the data", then here is one method.
First, you can get the four quarters by doing:
select distinct p_year, p_quarter
from production p
order by p_year desc, p_quarter desc
limit 4;
You can enumerate them using variables:
select p_year, p_quarter, (#rn := #rn + 1) as enum
from (select distinct p_year, p_quarter
from production p
order by p_year desc, p_quarter desc
limit 4
) p cross join
(select #rn := 0) params
Then, you can use this in a query to pivot the data:
select p.person_id,
sum(case when seqnum = 1 then num else 0 end) as num_quarter_1,
sum(case when seqnum = 2 then num else 0 end) as num_quarter_2,
sum(case when seqnum = 3 then num else 0 end) as num_quarter_3,
sum(case when seqnum = 4 then num else 0 end) as num_quarter_4
from production p join
(select p_year, p_quarter, (#rn := #rn + 1) as seqnum
from (select distinct p_year, p_quarter
from production p
order by p_year desc, p_quarter desc
limit 4
) p cross join
(select #rn := 0) params
) yq
using (p_year, p_quarter)
group by p.person_id;
EDIT:
If you are defining the quarters based on the current quarter, you can do something quite similar:
select p.person_id,
sum(case when seqnum = 1 then num else 0 end) as num_quarter_1,
sum(case when seqnum = 2 then num else 0 end) as num_quarter_2,
sum(case when seqnum = 3 then num else 0 end) as num_quarter_3,
sum(case when seqnum = 4 then num else 0 end) as num_quarter_4
from production p join
(select year(curdate()) as p_year, quarter(curdate()) as p_quarter, 1 as seqnum union all
select year(curdate() - interval 1 quarter) as p_year, month(curdate() - interval 1 quarter) as p_quarter, 2 as seqnum union all
select year(curdate() - interval 2 quarter) as p_year, month(curdate() - interval 2 quarter) as p_quarter, 3 as seqnum union all
select year(curdate() - interval 2 quarter) as p_year, month(curdate() - interval 3 quarter) as p_quarter, 4 as seqnum
) yq
using (p_year, p_quarter)
group by p.person_id;
There are other approaches, such as:
select person_id,
sum(case when year(curdate()) = p_year and quarter(curdate()) = p_quarter
then num else 0
end) as num_quarter_1,
sum(case when year(curdate() - interval 1 quarter) = p_year and quarter(curdate() - interval 1 quarter) = p_quarter
then num else 0
end) as num_quarter_2,
sum(case when year(curdate() - interval 2 quarter) = p_year and quarter(curdate() - interval 2 quarter) = p_quarter
then num else 0
end) as num_quarter_3,
sum(case when year(curdate() - interval 3 quarter) = p_year and quarter(curdate() - interval 3 quarter) = p_quarter
then num else 0
end) as num_quarter_4
from production p
group by person_id;
SELECT
person_id,
SUM((p_quarter = 1) * num) as num_quarter_1,
SUM((p_quarter = 2) * num) as num_quarter_2,
SUM((p_quarter = 3) * num) as num_quarter_3,
SUM((p_quarter = 4) * num) as num_quarter_4
FROM production
WHERE p_year = 2018
GROUP BY person_id;

MySQL Group By Query X or not X

This should be easy but I have not found the answer. I have a query that is running a count and I want to group this within the query into those where State = 1 and State <>1 and have the sum for each group.
SELECT count(`id_job`) as count, `state`
FROM job_table
GROUP BY `state`;
You can query a boolean expression and group by it too:
SELECT state = 1, COUNT(*)
FROM job_table
GROUP BY state = 1
SELECT count(id_job) as count,state FROM job_table
GROUP BY case when state = 1 then 1 else 0
In MySQL, you can easily put these into columns:
select sum( state = 1 ) as state_1,
sum( state <> 1 ) as state_2
from job_table;
If state can be NULL, you want to be a bit careful. The second condition is safer as:
select sum( state = 1 ) as state_1,
sum( not state <=> 1 ) as state_2
from job_table;
Try this
SELECT SUM(CASE WHEN State=1 THEN 1 END) as count1,
SUM(CASE WHEN State<>1 THEN 1 END) as count2,
State
FROM job_table
GROUP BY state

Concat rows with the same user

I have the query:
SELECT e.id_taxe
, u.nm_user
, dt_taxe
, SUM(e.vl_taxe) as vl_taxe
FROM taxe as e
JOIN user as u
ON u.id_user = e.id_user
WHERE id_enterprise = 86
AND dt_taxe BETWEEN '2017-01-01' AND '2017-03-31'
AND lg_cancel = 0
GROUP
BY e.dt_taxe
, e.id_user
ORDER
BY e.id_user
, e.dt_taxe
As result, I have:
id_taxe nm_user dt_taxe vl_taxe
728 Maria 2017-01-01 17091.07048034668
727 Maria 2017-02-01 14091.07048034668
721 Maria 2017-03-01 1021.07048034668
731 Pedro 2017-01-01 16353.569854736328
732 Pedro 2017-02-01 6353.56231239
How I can concat the rows, to get the result:
id_taxe nm_user dt_taxe vl_taxe
728 Maria 2017-01-01 17091.07048034668 ,
2017-02-01 14091.07048034668,
2017-03-01 1021.07048034668
731 Pedro 2017-01-01 16353.569854736328,
2017-02-01 6353.56231239
With a database like PostgreSQL this could be done with window functions but MySQL has no support for window functions so you cannot do that
This type of operation is usually best done at the application layer. Your result set is not very SQLish, because ordering is very important.
However, you can do this with variables:
SELECT (CASE WHEN rn = 1 THEN id_taxe END) as id_taxe,
(CASE WHEN rn = 1 THEN nm_user END) as num_user,
dt_taxe, vl_taxe
FROM (SELECT eu.*,
(#rn := if(#u = e.id_user, #rn + 1,
if(#u := e.id_user, 1, 1)
)
) as rn
FROM (SELECT e.id_taxe, u.nm_user, e.dt_taxe, SUM(e.vl_taxe) as vl_taxe
FROM taxe e INNER JOIN
user u
ON u.id_user = e.id_user
WHERE id_enterprise = 86 AND
dt_taxe BETWEEN '2017-01-01' AND '2017-03-31' AND
lg_cancel = 0
GROUP BY e.dt_taxe, e.id_user
) eu CROSS JOIN
(SELECT #i := -1, #rn := 0)
ORDER BY id_user, dt_taxe
) eu;

Mysql Ranking on on Left JOIN

I have two tables (teams and matches) and I want calculate the game table using points and #rank position.
The tables are very simple: Teams and matches (battles). The last column in 'battle' means if the battle was played or not.
DROP TABLE IF EXISTS monsters;
DROP TABLE IF EXISTS battles;
CREATE TABLE monsters (id_monster int auto_increment primary key, monster varchar(50));
CREATE TABLE battles (id_battle int auto_increment primary key, monster_h int, monster_a int, score_a int, score_h int, played tinyint);
Here some fake data
(Edit: I've updated the script removing random score and apply fixed scored.)
INSERT INTO monsters (monster) VALUES ("Nembo"), ("Kid"), ("Captain"), ("OOI"), ("Koky"), ("Rudes");
INSERT INTO battles (monster_h, monster_a, score_h, score_a, played) VALUES
(1,2, 3, 2,1),
(2,1, 2, 2,1),
(3,4, 1, 0,1),
(4,3, 0, 1,1),
(5,6, 0, 0,0),
(6,5, 5, 2,1),
(1,3, 6, 0,1),
(3,1, 2, 7,1),
(2,5, 1, 1,1),
(6,4, 0, 0,0),
(4,6, 0, 0,0);
Here my question
As you can see the Points are calculated correctly and table is good! But... #rank I wrong! I think I should join it too. Can you help me on #rank ?
SELECT m.monster,
(SUM(CASE WHEN
(b.monster_h = m.id_monster AND b.score_h > b.score_a) OR
(b.monster_a = m.id_monster AND b.score_a > b.score_h) THEN 3 ELSE 0 END) +
SUM(CASE WHEN
(b.monster_h = m.id_monster OR b.monster_a = m.id_monster) AND b.score_a = b.score_h THEN 1 ELSE 0 END)
) as pt,
(SUM(CASE WHEN
(b.monster_h= m.id_monster OR b.monster_a= m.id_monster) THEN 1 ELSE 0 END) ) as pl,
(#rank := #rank + 1) AS rank
FROM ( monsters as m )
LEFT JOIN battles as b ON m.id_monster IN (b.monster_a,b.monster_h)
,( SELECT #rank := 0 ) AS vars
where b.played = 1
GROUP BY m.monster
order by pt DESC, rand();
The result: with a wrong #rank
Monster, PTs, BATTLES, #RANK (good rank should be)
Nembo, 10, 4, 1 #1
Captain, 6, 4, 3 #2
Rudes, 3, 1, 6 #3
Kid, 2, 3, 2 #4
Koky, 1, 2, 5 #5
OOI, 0, 2, 4 #6
UPDATE
Use this data to have more monsters with same points and scores:
(1,2, 3, 2,1),
(2,1, 2, 2,1),
(3,4, 3, 0,1),
(4,3, 0, 1,1),
(5,6, 0, 0,0),
(6,5, 16, 12,1),
(1,3, 6, 0,1),
(3,1, 2, 7,1),
(2,5, 1,1,1),
(6,4, 0, 0,0),
(4,6, 0, 1,1),
(4,2,1,0,1),
(4,5,5,0,1),
(2,3,2,0,1),
(2,1,1,1,1);
and then use this script as Stefan said:
SELECT *, (#rank := #rank + 1) AS rank
FROM ( SELECT #rank := 0 ) as r ,
(SELECT m.monster,
(SUM(CASE WHEN
(b.monster_h = m.id_monster AND b.score_h > b.score_a) OR
(b.monster_a = m.id_monster AND b.score_a > b.score_h) THEN 3 ELSE 0 END) +
SUM(CASE WHEN
(b.monster_h = m.id_monster OR b.monster_a = m.id_monster) AND
b.score_a = b.score_h THEN 1 ELSE 0 END)) as pt,
(SUM(CASE WHEN
(b.monster_h= m.id_monster OR b.monster_a= m.id_monster) THEN 1 ELSE 0 END)) as pl,
(SUM(CASE WHEN
(b.monster_h= m.id_monster ) THEN b.score_h ELSE 0 END) +
SUM(CASE WHEN
(b.monster_a= m.id_monster ) THEN b.score_a ELSE 0 END)) as scored
, floor(rand()*1000) as coin
FROM monsters as m
LEFT JOIN battles as b ON m.id_monster IN (b.monster_a,b.monster_h)
where b.played = 1
GROUP BY m.monster) as result
order by result.pt DESC, scored DESC , coin DESC
The table is ordered by POINTS as first criteria, then scored. And it works. But if I use the "coin" as 3th criteria.... it doesn't works!
Great that you included all scripts to recreate your situation.
Put your results from your current query into a subquery, the trick with the #rank doesn't work with GROUP BY.
With this script I get the correct results:
SELECT *, (#rank := #rank + 1) AS rank
FROM ( SELECT #rank := 0 ) as r ,
(SELECT m.monster,
(SUM(CASE WHEN
(b.monster_h = m.id_monster AND b.score_h > b.score_a) OR
(b.monster_a = m.id_monster AND b.score_a > b.score_h) THEN 3 ELSE 0 END) +
SUM(CASE WHEN
(b.monster_h = m.id_monster OR b.monster_a = m.id_monster) AND
b.score_a = b.score_h THEN 1 ELSE 0 END)) as pt,
(SUM(CASE WHEN
(b.monster_h= m.id_monster OR b.monster_a= m.id_monster) THEN 1 ELSE 0 END)) as pl
FROM monsters as m
LEFT JOIN battles as b ON m.id_monster IN (b.monster_a,b.monster_h)
where b.played = 1
GROUP BY m.monster) as result
order by result.pt DESC