Select change column value if in list - mysql

I am trying to query my table to count the number of votes and if the voting method is in list ['C', 'M', 'S', 'L', 'T', 'V', 'B', 'E'] then count it as one and replace the voting_method to 'L'.
Right now I have the following query which returns the right results but doesn't take care of the duplicates.
select `election_lbl`, `voting_method`, count(*) as numVotes
from `gen2014` group by `election_lbl`, `voting_method` order by `election_lbl` asc
election_lbl voting_method numVotes
2014-09-04 M 1
2014-09-05 M 2
2014-09-05 S 1
2014-09-08 C 16
2014-09-08 M 5
2014-09-08 S 9
2014-09-09 10 5
2014-09-09 C 46
2014-09-09 M 4
2014-09-09 S 5
2014-09-10 C 92
2014-0g-10 M 3
2014-09-10 S 7
2014-09-11 C 96
2014-09-11 M 3
2014-09-11 S 2
2014-09-12 C 104
2014-09-12 M 10
2014-09-12 S 3
2014-09-15 C 243
2014-09-15 M 18
2014-09-15 S 3
2014-09-16 10 1
2014-09-16 C 161
2014-09-16 M 4
2014-09-16 S 3
2014-09-17 C 157
2014-09-17 M 5
2014-09-17 S 12
You can see that for 2014-09-05 I have two voting_method M and S both of which is in the list. I want the ideal result to remove the duplicate date field if the values are in the list. So it would be 2014-09-05 'L' 3. I don't want the vote for that date to disappear so the results should count them as one.
Changed the query to this but mysql says wrong syntax.
select `election_lbl`, `voting_method`, count(*) as numVotes from `gen2014`
(case `voting_method` when in ('C', 'M', 'S', 'L', 'T', 'V', 'B', 'E')
then 'L' END) group by `election_lbl`, `voting_method` order by `election_lbl` asc
Table Schema
CREATE TABLE `gen2014` (
`voting_method` varchar(255) DEFAULT NULL,
`election_lbl` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

SELECT election_lbl
, CASE WHEN voting_method IN ('C','M','S','L','T','V','B','E')
THEN 'L'
ELSE voting_method END my_voting_method
, COUNT(*)
FROM my_table
GROUP
BY my_voting_method -- or vice
, election_lbl; -- versa

If you just want the total votes using those methods for each date, listed as method 'L', then do not include method in the group by, and have the SELECT select 'L' as voting_method
select `election_lbl`, 'L' AS `voting_method`, count(*) as numVotes
from `gen2014`
where voting_method IN ('C', 'M', 'S', 'L', 'T', 'V', 'B', 'E')
group by `election_lbl`
order by `election_lbl` asc

select x.`election_lbl`, x.`voting_method`, count(*) as numVotes
from (
select `election_lbl`,
CASE when `voting_method` in ('C', 'M', 'S', 'L', 'T', 'V', 'B', 'E')
then 'L'
else `voting_method`
END as `voting_method`
from `gen2014`) x
group by x.`election_lbl`, x.`voting_method`
order by x.`election_lbl` asc

Related

How to get records based on particular column when multiple condition meets

I am trying to get the records, who are female (F) candidates, with greater than equal to 4 YOE and located in IN and CN and Salary greater than 50,000.
Two conditions am trying to meet here:
Higher the YOE, higher the preference
Between CN and IN, high preference are given to IN
It can be either be MySQL or PostgreSQL.
TABLE - 1 (ProfileDetails)
id first_name last_name email gender country_code
1 A B abc F IN
2 C D def F CN
3 E F ghi F IN
4 G H klm F CN
5 J K xyz F IN
6 X Y stu M IN
7 Z O cdf M UN
8 O N psq F CN
9 M T tql F IN
TABLE - 2 (SalaryDetails)
id YOE Salary
1 4 70000
2 10 60000
3 10 60000
4 11 55000
5 11 55000
6 5 70000
7 4 90000
8 6 55000
9 5 50000
Expected Output:
id first_name last_name email gender country_code YOE Salary
1 A B abc F IN 4 70000
3 E F ghi F IN 10 60000
5 J K xyz F IN 11 55000
8 O N psq F CN 6 55000
This is the query I have tried so far:
select p.id
, p.first_name
, p.last_name
, p.email
, p.gender
, p.country_code
, s.YOE
, s.Salary
from ProfileDetails p
left
join SalaryDetails s
ON p.id = s.id
where p.gender = 'F'
and s.YOE >=4
and p.country_code in ("CN","IN")
and s.Salary > 50000
order
by s.YOE DESC
Tables:
CREATE TABLE ProfileDetails
(id int not null, first_name varchar(7) not null, last_name varchar(7) not null, email varchar(7) not null, gender varchar(7) not null, country_code varchar(7) not null,
PRIMARY KEY (id));
CREATE TABLE SalaryDetails
(id int not null, company_name varchar(7) not null, YOE int not null, Salary int not null,
PRIMARY KEY (id));
INSERT INTO ProfileDetails
(id, first_name, last_name,email,gender,country_code)
VALUES
(1231, 'A', 'B', 'abc', 'F', 'IN'),
(1233, 'C', 'D', 'cde', 'F', 'CN'),
(1276, 'E', 'F', 'efg', 'F', 'IN'),
(1298, 'G', 'H', 'ghi', 'F', 'CN'),
(1401, 'I', 'J', 'ijk', 'F', 'IN'),
(1408, 'K', 'L', 'klm', 'F', 'IN'),
(1752, 'M', 'N', 'mno', 'M', 'UN'),
(1121, 'O', 'P', 'opq', 'F', 'CN'),
(1750, 'Q', 'R', 'qrs', 'F', 'IN'),
(2113, 'S', 'T', 'stu', 'F', 'CN')
;
INSERT INTO SalaryDetails
(id, company_name,YOE,Salary)
VALUES
(1231, 'Voonix', 4, 55000),
(1233, 'Realme', 10, 60000),
(1276, 'Apple', 10, 60000),
(1298, 'Iphone', 11, 55000),
(1401, 'Samsung', 11, 55000),
(1408, 'alpha', 5 , 70000),
(1752, 'bets', 4, 90000),
(1121, 'Gamma', 6, 55000),
(1750, 'Theta', 5, 55000),
(2113, 'Cosine', 4,55000)
;
Fiddle for MySQL - http://sqlfiddle.com/#!9/e0f009a
PostgreSQL
select *
from (
select *
,row_number() over (
partition by s.yoe
,s.salary order by p.country_code in ('IN','CN')) r
from profiledetails p
inner join salarydetails s on s.id = p.id
where s.salary > 50000
and p.gender = 'F'
and s.yoe >= 4
) t
where r = 1
Window Function References - 1 & 2

Condition on multiple values in the same column in SQL

Firstly, thanks in advance for helping. This will be my first question on SOF.
I have the following SQL database tables.
qualificationTable:
QualId studentNo CourseName Percentage
1 1 A 91
2 1 B 81
3 1 C 71
4 1 D 61
5 2 A 91
6 2 B 81
7 2 C 71
8 2 D 59
testTable:
TestId studentNo testNo Percentage dateTaken
1 1 1 91 2016-05-02
2 1 2 41 2015-05-02
3 1 3 71 2016-04-02
4 1 1 95 2014-05-02
5 1 2 83 2016-01-02
6 1 3 28 2015-05-02
7 2 1 90 2016-05-02
8 2 2 99 2016-05-02
9 2 3 87 2016-05-02
I have the minimum percentages specified for courses A, B, C and D individually. I need to search for students, meeting the minimum criteria for ALL the courses.
Part-2:
That student should also match the criteria (minimum percentages specified individually for the three tests- 1,2 and 3) in testTable.
In other words, if a student matches the minimum criteria (percentage) specified individually for all the courses, he should be selected. Now, same goes for the testTable, that particular student (who got selected in qualificationTable) should have the minimum criteria (percentage) specified individually for the three tests (1,2 and 3) in testNo column.
Edit:
I have updated the testTable, now there are multiple tests for a particular student. I need to check if the student meets the minimum required percentage specified for all the 3 tests, however, only the most recently taken test in each no (1,2 and 3) should count. If the student does not meet the minimum criteria specified for the most recent test, he should not be included.
Test Case:
Minimum qualification percentage required:
Course A: 90 Course B: 80 Course C: 70 Course D: 60
Minimum tests percentage required:
Test 1: 90 Test 2: 80 Test 3: 70
Expected Output
studentNo
1
Cheers
I've just figured it out for your sample data and Test Case:
Minimum qualification percentage required:
Course A: 90 Course B: 80 Course C: 70 Course D: 60
Minimum tests percentage required:
Test 1: 90 Test 2: 80 Test 3: 70
Try this, may help for you;)
SQL Fiddle
MySQL Schema:
CREATE TABLE qualificationTable
(`QualId` int, `studentNo` int, `CourseName` varchar(1), `Percentage` int)
;
INSERT INTO qualificationTable
(`QualId`, `studentNo`, `CourseName`, `Percentage`)
VALUES
(1, 1, 'A', 91),
(2, 1, 'B', 81),
(3, 1, 'C', 71),
(4, 1, 'D', 61),
(5, 2, 'A', 91),
(6, 2, 'B', 81),
(7, 2, 'C', 71),
(8, 2, 'D', 50)
;
CREATE TABLE testTable
(`TestId` int, `studentNo` int, `testNo` int, `Percentage` int)
;
INSERT INTO testTable
(`TestId`, `studentNo`, `testNo`, `Percentage`)
VALUES
(1, 1, 1, 91),
(2, 1, 2, 81),
(3, 1, 3, 71),
(4, 2, 1, 80),
(5, 2, 2, 99),
(6, 2, 3, 87)
;
Query 1:
select t1.studentNo
from
(
select studentNo from qualificationTable
where (CourseName = 'A' and Percentage >= 90)
or (CourseName = 'B' and Percentage >= 80)
or (CourseName = 'C' and Percentage >= 70)
or (CourseName = 'D' and Percentage >= 60)
group by studentNo
having count(1) = 4
) t1 join
( select studentNo from testTable
where (testNo = '1' and Percentage >= 90)
or (testNo = '2' and Percentage >= 80)
or (testNo = '3' and Percentage >= 70)
group by studentNo
having count(1) = 3
) t2 on t1.studentNo = t2.studentNo
I just pick t1 one of these two subquery to explain how it works:
GROUP BY can get us a result like this,
| studentNo |
|-----------|
| 1 |
| 2 |
COUNT will get us total count of each group, for your sample data, studentNo(1) is 4, studentNo(2) is 4 as well, but we also has where clause here, so by these criteria, we can find which matched are following record,
(1, 1, 'A', 91),
(2, 1, 'B', 81),
(3, 1, 'C', 71),
(4, 1, 'D', 61),
(5, 2, 'A', 91),
(6, 2, 'B', 81),
(7, 2, 'C', 71)
And this means COUNT will give us studentNo(1) to 4, studentNo(2) to 3, so when mysql run having count(1) = 4, this subquery only return us studentNo(1)
Subquery t2 works like that, and when join these two subquery by studentNo, it will return what you expected result.
Results:
| studentNo |
|-----------|
| 1 |
Edited:
select t1.studentNo
from
(
select studentNo from qualificationTable
where (CourseName = 'A' and Percentage >= 90)
or (CourseName = 'B' and Percentage >= 80)
or (CourseName = 'C' and Percentage >= 70)
or (CourseName = 'D' and Percentage >= 60)
group by studentNo
having count(1) = 4
) t1 join
( select studentNo
from (
select *
from testTable
where (testNo, dateTaken) in (
select testNo, Max(dateTaken) from testTable group by testNo
)
) tmp
where (testNo = '1' and Percentage >= 90)
or (testNo = '2' and Percentage >= 80)
or (testNo = '3' and Percentage >= 70)
group by studentNo
having count(1) = 3
) t2 on t1.studentNo = t2.studentNo
First find students who does not qualify the minimum percentage.
select distinct studentNo
from stdqualificationmaster
where case when CourseName='A' and Percentage<90 then 'F'
when CourseName='B' and Percentage<80 then 'F'
when CourseName='C' and Percentage<70 then 'F'
when CourseName='D' and Percentage<60 then 'F'
end='F'
As a second step we can use above unqualified students result set as filter for required result set.
select * from stdqualificationmaster where studentNo not in
( select distinct studentNo
from stdqualificationmaster
where case when CourseName='A' and Percentage<90 then 'F'
when CourseName='B' and Percentage<80 then 'F'
when CourseName='C' and Percentage<70 then 'F'
when CourseName='D' and Percentage<60 then 'F'
end='F')

How can I eliminate duplicate subquery/expression in DATEDIFF?

My database is a sample of login dates. I want to calculate a datediff between consecutive login dates on different rows. For example:
user_id login_date
1 2012-05-22
1 2012-05-25
/* difference is 3 days */
I was able to figure out two queries to do this calculation, but in both queries I needed to duplicate a subquery/expression to get my desired results.
I tried to use 'nextdate' in the datediff, but get an error:
#1054 - Unknown column 'nextdate' in 'field list'
Is there a way to eliminate the duplication? A completely new query is acceptable if it produces the desired results.
Sample database
CREATE TABLE IF NOT EXISTS `tbl` (
`user_id` int(11) DEFAULT NULL,
`login_date` date DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `tbl` (`user_id`, `login_date`) VALUES
(1, '2012-04-01'),
(1, '2012-04-25'),
(1, '2012-05-03'),
(1, '2012-05-04'),
(1, '2012-05-05'),
(1, '2012-05-06'),
(1, '2012-05-07'),
(1, '2012-05-09'),
(1, '2012-05-10'),
(1, '2012-05-11'),
(1, '2012-05-12'),
(1, '2012-05-16'),
(1, '2012-05-19'),
(1, '2012-05-20'),
(1, '2012-05-21'),
(1, '2012-05-22'),
(1, '2012-05-25'),
(2, '2012-04-02'),
(2, '2012-04-03'),
(2, '2012-04-04'),
(2, '2012-05-04'),
(2, '2012-05-06'),
(2, '2012-05-08'),
(2, '2012-05-09'),
(2, '2012-05-11'),
(2, '2012-05-17'),
(2, '2012-05-18'),
(2, '2012-05-19'),
(2, '2012-05-20'),
(2, '2012-05-21'),
(2, '2012-05-22'),
(2, '2012-05-25'),
(2, '2012-05-26'),
(2, '2012-05-27'),
(2, '2012-05-28'),
(2, '2012-05-29'),
(2, '2012-05-30'),
(2, '2012-05-31'),
(2, '2012-06-01'),
(2, '2012-06-02');
working query #1
SELECT
a.`user_id`,
a.`login_date`,
(SELECT
MIN(b.`login_date`)
FROM `tbl` b
WHERE a.`login_date` < b.`login_date`
AND a.`user_id` = b.`user_id`
) AS `nextdate`,
DATEDIFF((SELECT
MIN(b.`login_date`)
FROM `tbl` b
WHERE a.`login_date` < b.`login_date`
AND a.`user_id` = b.`user_id`
), a.`login_date`) AS `timespan`
FROM `tbl` a
WHERE 1=1
AND (a.`login_date` >= '2012-05-10' AND a.`login_date` <= '2012-05-25')
HAVING `nextdate` IS NOT NULL
ORDER BY a.`user_id` ASC, a.`login_date` ASC
working query #2
SELECT
a.`user_id`,
a.`login_date`,
MIN(b.`login_date`) AS `nextdate`,
DATEDIFF(MIN(b.`login_date`), a.`login_date`) AS `timespan`
FROM
(
SELECT
`user_id`,
`login_date`
FROM `tbl`
) a
JOIN
(
SELECT
`user_id`,
`login_date`
FROM `tbl`
) b
ON a.`user_id` = b.`user_id`
AND a.`login_date` < b.`login_date`
WHERE 1=1
AND (a.`login_date` >= '2012-05-10' AND a.`login_date` <= '2012-05-25')
GROUP BY a.`user_id`,a.`login_date`
desired results
user_id login_date nextdate timespan
1 2012-05-10 2012-05-11 1
1 2012-05-11 2012-05-12 1
1 2012-05-12 2012-05-16 4
1 2012-05-16 2012-05-19 3
1 2012-05-19 2012-05-20 1
1 2012-05-20 2012-05-21 1
1 2012-05-21 2012-05-22 1
1 2012-05-22 2012-05-25 3
2 2012-05-11 2012-05-17 6
2 2012-05-17 2012-05-18 1
2 2012-05-18 2012-05-19 1
2 2012-05-19 2012-05-20 1
2 2012-05-20 2012-05-21 1
2 2012-05-21 2012-05-22 1
2 2012-05-22 2012-05-25 3
2 2012-05-25 2012-05-26 1
This query is basically the same as your query #2, but just using a simple self join. The self join with group by and min(login_date) is about the simplest you can reduce this query down to, range scan on tbl a and then a key lookup on tbl b.
select a.user_id, a.login_date, min(b.login_date), datediff(min(b.login_date), a.login_date)
from tbl a
join tbl b on a.user_id = b.user_id and a.login_date < b.login_date
where (a.login_date >= '2012-05-10' AND a.login_date <= '2012-05-25')
group by a.user_id, a.login_date
order by a.user_id, a.login_date, b.login_date
;

mysql crosstab wrong sum total

I have a problem sum total in mysql crosstab.
my coding as the following:
SELECT IFNULL(Prtype,''Total'') as Prtype,sum(t.data) AS Total,',
SUM(IF(office ='A',`data`, NULL)) AS 'A',
SUM(IF(office ='B',`data`, NULL)) AS 'B',
SUM(IF(office ='C',`data`, NULL)) AS 'C',
FROM((SELECT Prtype, office,`data` as data
FROM TBLGETDATAALL_1 GROUP BY office,Prtype,data) t) GROUP BY Prtype
The problem is total not equal sum of all office.
Simple data:
Type Total A B C
P1 3 2 1 1
P2 6 2 2 1
P3 6 3 1 1
Simple data 2:
Total: 50,455
(1,333 1,352 1,216 2,127 1,520 2,700 1,174 1,250 2,458 1,374 2,877 970 2,458 2,930 1,365 2,655 1,184 3,001 2,421 2,689 2,220 1,590 2,678 2,212 1,329)=49083
why total=50,455 and sum each office=49083 ?
---------
table name
Prtype office data
p1 A 2
P2 B 3
P3 C 1
... ... .... ....
----------
Regards,
try this
select Prtype , A , B , C , sum( A +B +C) as total from (
SELECT IFNULL(Prtype,'Total') as Prtype ,
SUM(IF(office ='A',`data`, 0)) AS A,
SUM(IF(office ='B',`data`, 0)) AS B,
SUM(IF(office ='C',`data`, 0)) AS C
FROM TBLGETDATAALL_1
GROUP BY Prtype ) t
GROUP BY Prtype
DEMO HERE
Use 0 instead of null
SELECT IFNULL(Prtype,''Total'') as Prtype,sum(t.data) AS Total,',
SUM(IF(office ='A',`data`, 0)) AS 'A',
SUM(IF(office ='B',`data`, 0)) AS 'B',
SUM(IF(office ='C',`data`, 0)) AS 'C',
FROM((SELECT Prtype, office,`data` as data
FROM TBLGETDATAALL_1 GROUP BY office,Prtype,data) t) GROUP BY Prtype

How to improve ColdFusion MySQL query time?

I have 15 queries that generate data for a table on a page dynamically for the purpose of reports. Each query takes between 250 and 900ms which means a page loading time of 4 to 13 seconds depending on server load. The loading time is causing some users to the think the page is not going to load at all.
I was wondering if there was some way I could streamline the queries to give a more acceptable loading time. Here is one of the queries:
<cfquery datasource="MeetingDB" name="One">
SELECT COUNT( meetingID ) AS countatron
FROM case_meeting
WHERE meetingID
IN (
SELECT DISTINCT a.meetingID
FROM case_meeting a
INNER JOIN meeting b ON a.meetingID = b.meetingID
WHERE b.categoryID = '1'
AND SUBSTRING( meetingCode, 5, 2 )
BETWEEN 12
AND 22
AND SUBSTRING( meetingCode, 7, 2 )
BETWEEN 01
AND 12
AND SUBSTRING( meetingCode, 9, 2 )
BETWEEN 01
AND 31
)
AND caseID
IN (
'1', '2', '3', '28', '29', '30', '39', '40', '45'
)
GROUP BY meetingID
HAVING COUNT( caseID ) > 0 AND COUNT( caseID ) < 2
</cfquery>
<td><cfoutput> #One.recordcount# </cfoutput></td>
Try this query
SELECT COUNT( a.meetingID ) AS countatron
FROM case_meeting a, case_meeting b
WHERE a.meetingID = b.meetingID
AND b.categoryID = '1'
AND SUBSTRING( b.meetingCode, 5, 2 )
BETWEEN 12
AND 22
AND SUBSTRING( b.meetingCode, 7, 2 )
BETWEEN 01
AND 12
AND SUBSTRING( b.meetingCode, 9, 2 )
BETWEEN 01
AND 31
AND b.caseID
IN (
'1', '2', '3', '28', '29', '30', '39', '40', '45'
)
GROUP BY a.meetingID
HAVING COUNT( a.caseID ) = 1
Might be worth trying to do joins on subselects rather than using IN.
Something like this:-
SELECT COUNT( case_meeting.meetingID ) AS countatron
FROM case_meeting
INNER JOIN (
SELECT DISTINCT a.meetingID
FROM case_meeting a
INNER JOIN meeting b ON a.meetingID = b.meetingID
WHERE b.categoryID = '1'
AND SUBSTRING( meetingCode, 5, 2 ) BETWEEN 12 AND 22
AND SUBSTRING( meetingCode, 7, 2 ) BETWEEN 01 AND 12
AND SUBSTRING( meetingCode, 9, 2 ) BETWEEN 01 AND 31
) Sub1
ON case_meeting.meetingID = Sub1.meetingID
INNER JOIN (
SELECT meetingID, COUNT( caseID ) AS MeetingCaseCount
FROM case_meeting
WHERE caseID IN ('1', '2', '3', '28', '29', '30', '39', '40', '45')
GROUP BY meetingID
) Sub2
ON case_meeting.meetingID = Sub2.meetingID
WHERE Sub2.MeetingCaseCount > 0 AND Sub2.MeetingCaseCount < 2
GROUP BY case_meeting.meetingID
If you have a lot of queries that don't depend on each other, then take a look at cfthread. This will allow you to run the queries concurrently.
Make sure you test it thoroughly. I've had one experience where the use of cfthread had adverse effects on a database server.
It's still worth a shot though.
i suppose creating a procedure in the MySql and invoking it with arguments from my sql is most appropriate. you could also create views with dynamic param