The query is not giving a desired output which I want - mysql

Query with OR which outputs wrong
SELECT DISTINCT
sm___employees.id,
sm___employees.employee_code,
sm___employees.leaving_date,
sm___employees.name_of_employee,
sm___employees.position,
sm___employees.rating,
sm___employees.entry_date
FROM
sm___employees
JOIN
sm___employee_skills
ON
sm___employees.id=sm___employee_skills.employee_id
WHERE
((sm___employee_skills.skill_id=1 AND sm___employee_skills.ans LIKE '%MBA%')
**OR**
(sm___employee_skills.skill_id=5 AND sm___employee_skills.ans IN (3)))
AND
sm___employees.rating IN (1)
ORDER BY
sm___employee_skills.date DESC
But I want it by And
SELECT DISTINCT
sm___employees.id,
sm___employees.employee_code,
sm___employees.leaving_date,
sm___employees.name_of_employee,
sm___employees.position,
sm___employees.rating,
sm___employees.entry_date
FROM
sm___employees
JOIN
sm___employee_skills
ON
sm___employees.id=sm___employee_skills.employee_id
WHERE
((sm___employee_skills.skill_id=1 AND sm___employee_skills.ans LIKE '%MBA%')
**AND**
(sm___employee_skills.skill_id=5 AND sm___employee_skills.ans IN (3)))
AND
sm___employees.rating IN (1)
ORDER BY
sm___employee_skills.date DESC
When am using first query with OR of MBA or 3, It gives me result for both which is correct as per OR operation
I want only those records which are having MBA AND 3 which gives me blank records when there are records available with this comparison
So please help me to resolve this.
Thank you in advance

To start with: DISTINCT often indicates a badly written query. This is the case here. You are joining records only to dismiss them later. If you want employee records, then select from the employee table. If you have criteria on the skills table check this in the WHERE clause. Don't join.
Then the WHERE clause looks at one row at a time. So neither skill_id = ... AND skill_id = ... nor skill_id = ... OR skill_id = ... can work for you. You must look up the skills table twice:
SELECT
id,
employee_code,
leaving_date,
name_of_employee,
position,
rating,
entry_date
FROM sm___employees
WHERE rating IN (1)
AND id IN
(
SELECT employee_id
FROM sm___employee_skills
WHERE skill_id = 1 AND ans LIKE '%MBA%'
)
AND id IN
(
SELECT employee_id
FROM sm___employee_skills
WHERE skill_id = 5 AND ans IN (3)
);
And here is a way to look up skills just once:
SELECT
id,
employee_code,
leaving_date,
name_of_employee,
position,
rating,
entry_date
FROM sm___employees
WHERE rating IN (1)
AND id IN
(
SELECT employee_id
FROM sm___employee_skills
WHERE (skill_id = 1 AND ans LIKE '%MBA%')
OR (skill_id = 5 AND ans IN (3))
GROUP BY employee_id
HAVING COUNT(DISTINCT skill_id) = 2 -- both skills
);
It seems strange though that you consider ans to be a string in one place (ans LIKE '%MBA%') and a number in another (ans IN (3)).
UPDATE: If you want to sort by skill date, you should consider by which skill's date. For this to happen, you would join, but not join the skills table, but the skills aggregate result. E.g.:
SELECT
e.id,
e.employee_code,
e.leaving_date,
e.name_of_employee,
e.position,
e.rating,
e.entry_date
FROM sm___employees e
JOIN
(
SELECT employee_id, MAX(date) AS max_date
FROM sm___employee_skills
WHERE (skill_id = 1 AND ans LIKE '%MBA%')
OR (skill_id = 5 AND ans = 3)
GROUP BY employee_id
HAVING COUNT(DISTINCT skill_id) = 2 -- both skills
) s ON s.employee_id = e.id
WHERE e.rating = 1
ORDER BY s.max_date;

Please try this :
SELECT DISTINCT
sm1.id,
sm1.employee_code,
sm1.leaving_date,
sm1.name_of_employee,
sm1.position,
sm1.rating,
sm1.entry_date
FROM sm___employees sm1
LEFT JOIN sm___employee_skills sm2 ON sm1.id = sm2.employee_id
WHERE ((sm2.skill_id=1 AND sm2.ans LIKE '%MBA%')
AND (sm2.skill_id=1 AND sm2.ans=3))
AND sm1.rating IN (1)
ORDER BY sm2.date DESC;

Related

SUM(DISTINCT) on the same table?

I currently have table as below:
By executing the code as:
SELECT FromLedger, SUM(TransactionVal)
FROM Journal
WHERE FromGroup = 'Asset'
GROUP BY FromLedger
I get the result as:
Simlarly, I get the another set by executing:
SELECT ToLedger, SUM(TransactionVal)
FROM Journal
WHERE ToGroup = 'Asset'
GROUP BY ToLedger
I want to get a single table combining the both table with Group By Ledger Id and another column as the difference between the SUM of the above 2 table. In other words, I am looking for table as below
How do I get it please?
You can join the 2 queries like this:
SELECT t.ToLedger UniqueLedgerId, t.total - f.total ClosingBalance
FROM (SELECT ToLedger, SUM(TransactionVal) total FROM Journal WHERE ToGroup = 'Asset' GROUP BY ToLedger) t
INNER JOIN (SELECT FromLedger, SUM(TransactionVal) total FROM Journal WHERE FromGroup = 'Asset' GROUP BY FromLedger) f
ON f.FromLedger = t.ToLedger
or use UNION ALL and then aggregate:
SELECT t.UniqueLedgerId, SUM(t.TransactionVal) ClosingBalance
FROM (
SELECT ToLedger UniqueLedgerId, TransactionVal FROM Journal WHERE ToGroup = 'Asset'
UNION ALL
SELECT FromLedger, -TransactionVal FROM Journal WHERE FromGroup = 'Asset'
) t
GROUP BY t.UniqueLedgerId

joint three tables in Mysql

I have three tables employee, promotion and punishment
Employee’s table structure something like this
Id int
Fullname varchar
...............
promotionDate date
Promotion’s table structure is like this
id int
emp_id int
directorateDate date
And punishment’s table structure is like this
id int
emp_id int
direcotorateDate date
Let’s say employee table has 200 records, each month a group of employees have promotion (after serving one year), I want to get the list of all employees in the current month that get promotion
I can easily get the list by this query
SELECT *
FROM employee
WHERE MONTH(promotionDate) = MONTH(CURRENT_DATE())
AND YEAR(promotionDate) = YEAR(CURRENT_DATE())
My question is
I want to count number of punishments and promotions each employee got in the current year from punishment and promotion table respectively
I did this query but it did not get right results
SELECT e.fullname , COUNT(punish.emp_id) as siza ,COUNT(pro.emp_id) as supas
FROM emp_employee as e
LEFT JOIN emp_punishment as punish on punish.emp_id=e.id
LEFT JOIN emp_promotion as pro on e.id=pro.emp_id
WHERE ((MONTH(e.promotionDate) = MONTH(CURRENT_DATE())
AND YEAR(e.promotionDate) = YEAR(CURRENT_DATE()))
AND ( YEAR(punish.directorate_date) = YEAR(CURRENT_DATE()) )
AND ( YEAR(pro.directorate_date) = YEAR(CURRENT_DATE()) )
GROUP BY e.fullname;
Any help please.
By joining directly the 3 tables you get duplicate rows.
Group by emp_id and aggregate separately each of the tables emp_punishment and emp_promotion and join the results to the table emp_employee.
select e.fullname, coalesce(pu.siza, 0) siza, coalesce(pr.supas, 0) supas
from emp_employee as e
left join (
select emp_id, count(*) siza
from emp_punishment
where year(directorate_date) = year(CURRENT_DATE)
group by emp_id
) pu on pu.emp_id = e.id
left join (
select emp_id, count(*) supas
from emp_promotion
where year(directorate_date) = year(CURRENT_DATE)
group by emp_id
) pr on pr.emp_id = e.id
I used only the condition:
where year(directorate_date) = year(CURRENT_DATE())
because in your question you say:
I want to count number of punishments and promotions each employee got in the current year from punishment and promotion
Removing MONTH() function, and moving each condition to their respective place, instead of within the WHERE clause should resolve the issue (Since, they're considered as if INNER JOINs with the current style ).
Only keep common column e.promotionDate within the WHERE clause :
SELECT e.fullname,
COUNT(punish.emp_id) as siza ,
COUNT(pro.emp_id) as supas
FROM emp_employee as e
LEFT JOIN emp_punishment as punish
ON punish.emp_id=e.id
AND YEAR(punish.directorate_date) = YEAR(CURRENT_DATE())
LEFT JOIN emp_promotion as pro
ON e.id=pro.emp_id
AND YEAR(pro.directorate_date) = YEAR(CURRENT_DATE()))
WHERE YEAR(e.promotionDate) = YEAR(CURRENT_DATE())
GROUP BY e.fullname;

Using select from another select in MySQL

I have the following query where I will have finally a 205 patient IDs to work with:
select
patient_id
FROM
visit
WHERE
month(visit.date_of_visit)=3
AND
year(visit.date_of_visit)=2018
AND
visit.visit_status='Active'
GROUP BY patient_id
I want to get all the 205 IDs and run them into other query to see how many diseases we have as cardio-vascular and then as respiratory disease.
My database structure is as the following:
What I want is to get for each patient id, what they are diagnosed at ONLY their first visit to the hospital (so here we will work with min(visit.date_of_visit))
The desired result for `diagnosis_name LIKE '%Cardio%':
E.g>:
Patients: 150 (Or something)
And the query is changed to get the respiratory info.
I tried the following for the Cardio diseases where I use select from select:
SELECT count(*)
FROM
(
select
min(visit.date_of_visit), visit_id, patient_id, count(*) as patientId
FROM
visit
WHERE
month(visit.date_of_visit)=3
AND
year(visit.date_of_visit)=2018
AND
visit.visit_status='Active'
GROUP BY patient_id
) as vid
LEFT JOIN
consultation ON consultation.visit_id=vid.visit_id
LEFT JOIN
diagnosis ON diagnosis.diagnosis_id=consultation.diagnosis_id
WHERE diagnosis.diagnosis_name LIKE '%Cardio%'
The result was: 5 which is a wrong number.
This can be done easily with PHP and MYSQL together but this will exhaust the server by repeating the same query for 205 times and increment a counter. So the desired result should be only done with MySQL.
Data example:
Visit Table
visit_id= 1; date_of_visit=2018-03-03; visit_reason=Active; ...;
patient_id=1234;
visit_id= 2; date_of_visit=2018-03-04; visit_reason=Active; ...;
patient_id=1239;
visit_id= 3; date_of_visit=2018-03-07; visit_reason=Active; ...;
patient_id=1234;
Consultation Table
consultation_id=1; ...; diagnosis_id=12; visit_id=1;...;
consultation_id=2; ...; diagnosis_id=12; visit_id=2;...;
Diagnosis Table
diagnosis_id=12; diagnosis_name: hypertension (cardio disease);
diagnosis_id=13; diagnosis_name: renal disease
By running the query to see patients who came to hospital and that they were diagnosed as having cardio disease in their initial first visit, the result should be in the example as 2 as you can see from the example where patient_id=1234 had 2 visits but I need to know what he had in his first one.
You can use window functions in MySQL 8+. But in older versions you need to calculate the value some other way.
The question for you is what you are counting:
SELECT COUNT(*) as num_diagnoses, COUNT(DISTINCT patient_id) as num_patients
FROM visit v JOIN
(SELECT patient_id,
MIN(v.date_of_visit) as min_dov
FROM visit v
WHERE v.date_of_visit >= '2018-03-01' AND
v.date_of_visit < '2018-04-01' AND
v.visit_status = 'Active'
) vf
ON v.patient_id = vf.patient_id AND v.date_of_visit = vf.min_dov JOIN
consultation c
ON c.visit_id = v.visit_id JOIN
diagnosis d
ON d.diagnosis_id = c.diagnosis_id
WHERE d.diagnosis_name LIKE '%Cardio%';
When working with dates, it is best to compare column values directly to dates, rather than dissecting them.
BRO, it works fine. Test it now on the live scenario.
SELECT count(*)
FROM
(
select
min(visit.date_of_visit) first_date, patient_id, count(*) as patientId
FROM
visit
WHERE
month(visit.date_of_visit)=3
AND
year(visit.date_of_visit)=2018
AND
visit.visit_status='Active'
GROUP BY patient_id
) as vid
INNER JOIN visit b ON
B.patient_id = vid.patient_id AND
B.date_of_visit = vid.first_date and
month(B.date_of_visit)=3 AND
year(B.date_of_visit)=2018 AND
B.visit_reason='Active'
INNER JOIN consultation ON
consultation.visit_id = B.visit_id
INNER JOIN diagnosis ON
diagnosis.diagnosis_id = consultation.diagnosis_id AND
diagnosis.diagnosis_name LIKE '%Cardio%'

Inner query is difficult to write

I have two tables:
customer with schema_id
Schema table has: schema_id, period, amt, updated_date
I need to take join of customer and schema but only retrieve the latest record joined and not the others.
customer table
cust_id name schema_id
1 ABC 1
Schema table
schema_id period amt updated_date
1 1 100 2010-4-1
1 2 150 2011-4-1
If you need the max(updated_date) for each schema_id, then you can use an subquery:
select c.cust_id, c.name, c.schema_id, s.period, s.amt, s.updated_date
from customer c
inner join
(
select s1.schema_id, s1.period, s1.amt, s1.updated_date
from `schemas` s1
inner join
(
select schema_id, max(updated_date) MaxDate
from `schemas`
group by schema_id
) s2
on s1.schema_id = s2.schema_id
and s1.updated_date = s2.maxdate
) s
on c.schema_id = s.schema_id
See SQL Fiddle with Demo
The subquery is then used in a join back to your table to return the rows that have the matching date and schema_id.
If I understood your problem, you need to take lastest register of the "schema".
I think you need to use max() function. So, try the query below:
select *
from customer c,
schema s
where c.schema_id = s.schema_id
and s.updated_date = ( select max(s2.updated_date)
from schema s2
where s2.schema_id = s.schema_id
)
Regards!
Edmilton

SQL Query / Slow

I have the below SQL code, this is from a MySQL database. Now it gives me the results I expect, however the query is slow and I think I should speed this query up before going any further.
The table agentstatusinformation has:
PKEY (Primary Key), userid (integer), agentstate (integer)
The table axpuser contains the users name:
PKEY (Primary Key) <-- this is the key for userid, loginid (usersname)
select distinct (select loginid from axpuser where axpuser.pkey = age.userid),
case
when agentstate = 1 then 'Ready'
when agentstate = 3 then 'Pause'
end as state
from agentstatusinformation age
where (userid, pkey) in
(select userid, max(pkey) from agentstatusinformation group by userid)
I am sure this can be improved upon, but I cannot see the wood for the trees.
Many thanks.
Not precisely certain this is what you want, but I think its close:
Select loginid, case when c.agentstate=1 Then 'Ready'
when c.agentstate=3 then 'Pause'
end state
from axpuser a
join (select userid, max(pkey) pkey
from agentstatusinformation
group by userid ) b
on a.userid=b.userid
join agentstatusinformation c
and b.pkey=c.pkey
This eliminates the subselect in the initial SELECT clause, and joins against the grouped stats information table. Hope this helps.
The problem with your query are your nested selects. In particular, the subquery in the IN clause is problematic in MySQL. It gets called for every row filtered by the where clause.
The following fixes this:
select distinct (select loginid from axpuser where axpuser.pkey = age.userid),
case
when agentstate = 1 then 'Ready'
when agentstate = 3 then 'Pause'
end as state
from agentstatusinformation age
where exists (select userid, max(pkey)
from agentstatusinformation a2
where a2.userid = age.userid
group by userid
having age.pkey = max(pkey))
You can make this run faster by creating an index on agenstatusinfromation(userid, pkey).
The nested select should not be causing a problem, as long as there is an index on axpuser.pkey. However, I think it is better form to put this in the FROM clause as a join:
select distinct axpuser.loginid,
case
when agentstate = 1 then 'Ready'
when agentstate = 3 then 'Pause'
end as state
from agentstatusinformation age left outer join
axpuser
on axpuser.key = age.userid
where exists (select userid, max(pkey)
from agentstatusinformation a2
where a2.userid = age.userid
group by userid
having age.pkey = max(pkey)
)
select ax.loginid,
case
when age.agentstate = 1 then 'Ready'
when age.agentstate = 3 then 'Pause'
end as state
from
agentstatusinformation age
join
axpuser ax
on age.userid = ax.userid and age.pkey=(select max(pkey) from agentstatusinformation group by userid)