where clause while joining select - mysql

I need to get payment sum for all period and for current month. There are two tables: category and transactions.
transactions:
id int(11)
category varchar(32)
dttm_added datetime
minus float
minus_currency varchar(32)
categories:
id int(11)
key varchar(32)
name varchar(50)
type varchar(1)
Here is my query:
select `key`, `id`, `name`, minus_month,month_cur
from `categories` as ct
left join (
select `category` as tr_ct_m, date_format(`dttm_trans`, '%Y%m') as dat, sum(`minus`) as minus_month, `minus_currency` as month_cur from `transactions` where dat = date_format(now(), '%Y%m')
) as tr_m on tr_m.tr_ct_m = ct.key
where `type` = '-'
I need to check clause in select before joining, because after sum of minus_month all dates are turning to Null.
Help plz

not sure if i understand you correctly,please try below:
select `ct`.`key`, `ct`.`id`, `ct`.`name`, tr_m.minus_month,tr_m.month_cur from
( select * from `categories` where `type` = '-') as ct
left join
( select `category` as tr_ct_m, date_format(`dttm_trans`, '%Y%m') as dat,
sum(`minus`) as minus_month, `minus_currency` as month_cur from `transactions`
where dat = date_format(now(), '%Y%m')
) as tr_m on tr_m.tr_ct_m = ct.key

Related

SQL query to select all rows with max column value

CREATE TABLE `user_activity` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`type` enum('request','response') DEFAULT NULL,
`data` longtext NOT NULL,
`created_at` datetime DEFAULT NULL,
`source` varchar(255) DEFAULT NULL,
`task_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
);
I have this data:-
Now I need to select all rows for user_id=527 where created_at value is the maximum. So I need the last 3 rows in this image.
I wrote this query:-
SELECT *
FROM user_activity
WHERE user_id = 527
AND source = 'E1'
AND task_name IN ( 'GetReportTask', 'StopMonitoringUserTask' )
AND created_at = (SELECT Max(created_at)
FROM user_activity
WHERE user_id = 527
AND source = 'E1'
AND task_name IN ( 'GetReportTask',
'StopMonitoringUserTask' ));
This is very inefficient because I am running the exact same query again as an inner query except that it disregards created_at. What's the right way to do this?
I would use a correlated subquery:
SELECT ua.*
FROM user_activity ua
WHERE ua.user_id = 527 AND source = 'E1' AND
ua.task_name IN ('GetReportTask', 'StopMonitoringUserTask' ) AND
ua.created_at = (SELECT MAX(ua2.created_at)
FROM user_activity ua2
WHERE ua2.user_id = ua.user_id AND
ua2.source = ua.source AND
ua2.task_name IN ( 'GetReportTask', 'StopMonitoringUserTask' )
);
Although this might seem inefficient, you can create an index on user_activity(user_id, source, task_name, created_at). With this index, the query should have decent performance.
Order by created_at desc and limit your query to return 1 row.
SELECT *
FROM user_activity
WHERE user_id = 527
AND source = 'E1'
AND task_name IN ( 'GetReportTask', 'StopMonitoringUserTask' )
ORDER BY created_at DESC
LIMIT 1;
I used EverSQL and applied my own changes to come up with this single-select query that uses self-join:-
SELECT *
FROM user_activity AS ua1
LEFT JOIN user_activity AS ua2
ON ua2.user_id = ua1.user_id
AND ua2.source = ua1.source
AND ua2.task_name IN ( 'GetReportTask', 'StopMonitoringUserTask' )
AND ua1.created_at < ua2.created_at
WHERE ua1.user_id = 527
AND ua1.source = 'E1'
AND ua1.task_name IN ( 'GetReportTask', 'StopMonitoringUserTask' )
AND ua2.created_at IS NULL;
However, I noticed that the response times of both queries were similar. I tried to use Explain to identify any performance differences; and from what I understood from its output, there are no noticeable differences because proper indexing is in place. So for readability and maintainability, I'll just use the nested query.

Load top 5 records per date

I have a table, in which there are date wise quiz score of different users. I want to load top 5 scorers for every date.
Table sample create statement:
CREATE TABLE `subscriber_score` (
`msisdn` varchar(25) COLLATE utf8_unicode_ci NOT NULL,
`date` date NOT NULL,
`score` int(11) NOT NULL DEFAULT '0',
`total_questions_sent` int(11) NOT NULL DEFAULT '0',
`total_correct_answers` int(11) NOT NULL DEFAULT '0',
`total_wrong_answers` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`msisdn`,`date`),
KEY `fk_subscriber_score_subscriber1` (`msisdn`),
CONSTRAINT `fk_subscriber_score_subscriber1` FOREIGN KEY (`msisdn`) REFERENCES `subscriber` (`msisdn`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Query which I have tried:
SELECT subscriber.msisdn AS msisdn,subscriber.name AS name,subscriber.gender AS gender,tmp2.score AS score,tmp2.date AS winning_date
FROM subscriber,
(SELECT msisdn,tmp.date,tmp.score
FROM subscriber_score,
(SELECT date,MAX(score) AS score
FROM subscriber_score
WHERE date > '2014-10-10' AND date < '2014-11-10' GROUP BY date)
tmp
WHERE subscriber_score.date=tmp.date AND subscriber_score.score=tmp.score)
tmp2
WHERE subscriber.msisdn=tmp2.msisdn ORDER BY winning_date
Actual output: Only one top scorer for every date is shown.
Wanted Output Top 5(or say 10) records for every date are required.
I think you can do this using variables to assign each row a row number, then filter the top 5 for each date.
SELECT s.name AS name,
s.gender AS gender,
s.msisdn,
ss.date,
ss.score
FROM ( SELECT ss.msisdn,
ss.score,
#r:= CASE WHEN ss.Date = #d THEN #r + 1 ELSE 1 END AS RowNum,
#d:= ss.date AS winning_date
FROM subscriber_score AS ss
CROSS JOIN (SELECT #d:= '', #r:= 0) AS v
WHERE ss.date > '2014-10-10'
AND ss.date < '2014-11-10'
ORDER BY ss.Date, ss.Score DESC
) AS ss
INNER JOIN Subscriber AS s
ON s.msisdn = ss.msisdn
WHERE ss.RowNum <= 5;
Example on SQL Fiddle
refer this query its not complete but hope it helps
SELECT SCORE
FROM table
WHERE date='somedate'
ORDER BY SCORE DESC LIMIT 5
select bc.msisdn msisdn,bc.name name,bc.gender gender,ab.score score,ab.date winning_date
(
select msisdn,date,score,
dense_rank() over (partition by date order by score desc) rnk
from subscriber_score
) ab,subscriber bc
where bc.msisdn=ab.msisdn and ab.rnk<=5
order by winning_date ;
This is how you can get solution of your problem in oracle sql.
try below
SELECT subscriber.msisdn AS msisdn,subscriber.name AS name,subscriber.gender AS gender,tmp2.score AS score,tmp2.date AS winning_date
FROM subscriber inner join
(select msisdn,date, score, ROW_NUMBER() OVER(PARTITION BY date ORDER BY score DESC) AS Row
FROM subscriber_score
WHERE date > '2014-10-10' AND date < '2014-11-10' GROUP BY date)
tmp
on subscriber.msisdn=tmp.msisdn and tmp.row<=5

MySQL How to select mulipie rows then send to another query

I hava a Table ,I can use
SELECT `event_id` FROM `event` WHERE 1
to get mulipie rows event_id , and I put event_id in this query
SET #event_id = XXXX;
SELECT * FROM(
(SELECT `event_id`,`author_id`,`latitude`,`longitude`,`type`,`rate`,`description`,`photo_id`, UNIX_TIMESTAMP(`timestamp`) AS `timestamp`
FROM `event`) AS `event`,
(SELECT COUNT(*) AS `thumb_up_count` FROM `thumb_event` WHERE `event_id` = #event_id AND `thumb_state` = 1) AS `thumb_up_count`,
(SELECT COUNT(*) AS `thumb_down_count` FROM `thumb_event` WHERE `event_id` = #event_id AND `thumb_state` = 2) AS `thumb_down_count`,
(SELECT IFNULL((SELECT `thumb_state` FROM `thumb_event` WHERE `event_id` = #event_id AND `user_id` = ?) , 0) AS `thumbed`) AS `thumbed`,
(SELECT COUNT(*) AS `comment_count` FROM `event_comment` WHERE `event_id` = #event_id) AS `comment_count`
) WHERE `event_id` = #event_id;
I can use SET #event_id = 0000; to set event_id , but how I can put all event_id from select in that query
If i understoods, maybe what you need it's an IN clause:
WHERE `event_id` IN (SELECT `event_id` FROM `event` WHERE 1);

MySQL - Want to Minus two SUM() values from two SELECT statements

I want to be able to grab the two values that I have generated in SUM() from two SELECT queries and minus these values in order to get the result (OutstandingFunds).
These are my two SELECT queries:
Statement (1):
SELECT SUM(Cf.Amount) AS ClearedFunds
FROM (
SELECT Amount FROM PAYMENT1 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT2 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT3 WHERE `Status` = "Cleared") AS Cf;
Statement (2):
SELECT SUM(Price) AS TotalSales
FROM PROPERTY
WHERE Status = “Sold”;
Thank you for your time
If you don't need to show the separate subtotal for total sales and cleared funds, you can do it like this:
SELECT SUM(Total.`Price`) AS ClearedFunds
FROM (
SELECT `Price` FROM PROPERTY WHERE `Status` = 'Sold'
UNION ALL
SELECT (`Amount` * -1) AS `Price` FROM PAYMENT1 WHERE `Status` = 'Cleared'
UNION ALL
SELECT (`Amount` * -1) AS `Price` FROM PAYMENT2 WHERE `Status` = 'Cleared'
UNION ALL
SELECT (`Amount` * -1) AS `Price` FROM PAYMENT3 WHERE `Status` = 'Cleared'
) AS Total;
I am assuming you are wanting to subtract cleared funds from total sales here.
You were almost there... here's the working SQL:
SELECT (SELECT SUM(Cf.Amount) AS ClearedFunds
FROM (
SELECT Amount FROM PAYMENT1 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT2 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT3 WHERE `Status` = "Cleared") as Cf)
- (SELECT SUM(Price) AS TotalSales
FROM PROPERTY
WHERE Status = "Sold") as Result;
Here's the SQL Fiddle so that you can play with test data: http://sqlfiddle.com/#!2/18677/11
You can use SELECT ... INTO ..., in your case:
SELECT SUM(Cf.Amount) AS ClearedFunds
FROM (
SELECT Amount FROM PAYMENT1 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT2 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT3 WHERE `Status` = "Cleared") INTO #cf;
SELECT SUM(Price) AS TotalSales
FROM PROPERTY
WHERE Status = “Sold” INTO #ts;
Then you can substract or use them in any other query, like this:
SELECT #ts - #cf;
Are you looking for this?
SELECT C.ClearedFunds - P.TotalSales
FROM (
SELECT SUM(Cf.Amount) AS ClearedFunds
FROM (
SELECT Amount FROM PAYMENT1 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT2 WHERE `Status` = "Cleared"
UNION ALL
SELECT Amount FROM PAYMENT3 WHERE `Status` = "Cleared") AS Cf) C,
(SELECT SUM(Price) AS TotalSales
FROM PROPERTY
WHERE Status = “Sold”) P
;

Count enumerated values?

If my table looks like this:
CREATE TABLE `daily_individual_tracking` (
`daily_individual_tracking_id` int(10) unsigned NOT NULL auto_increment,
`daily_individual_tracking_date` date NOT NULL default ''0000-00-00'',
`sales` enum(''no'',''yes'') NOT NULL COMMENT ''no'',
`repairs` enum(''no'',''yes'') NOT NULL COMMENT ''no'',
`shipping` enum(''no'',''yes'') NOT NULL COMMENT ''no'',
PRIMARY KEY (`daily_individual_tracking_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
basically the fields can be either yes or no.
How can I count how many yes's their are for each column over a date range?
Thanks!!
You can either run three queries like this:
SELECT COUNT(*)
FROM daily_individual_tracking
WHERE sales = 'YES'
AND daily_individual_tracking_date BETWEEN '2010-01-01' AND '2010-03-31'
Or if you want you can get all three at once like this:
SELECT (
SELECT COUNT(*)
FROM daily_individual_tracking
WHERE sales = 'YES'
AND daily_individual_tracking_date BETWEEN '2010-01-01' AND '2010-03-31'
) AS sales_count, (
SELECT COUNT(*)
FROM daily_individual_tracking
WHERE repairs = 'YES'
AND daily_individual_tracking_date BETWEEN '2010-01-01' AND '2010-03-31'
) AS repairs_count, (
SELECT COUNT(*)
FROM daily_individual_tracking
WHERE shipping = 'YES'
AND daily_individual_tracking_date BETWEEN '2010-01-01' AND '2010-03-31'
) AS shipping_count
Another way to do it is to use SUM instead of COUNT. You could try this too to see how it affects the performance:
SELECT
SUM(sales = 'YES') AS sales_count,
SUM(repairs = 'YES') AS repairs_count,
SUM(shipping = 'YES') AS shipping_count
FROM daily_individual_tracking
WHERE daily_individual_tracking_date BETWEEN '2010-01-01' AND '2010-03-31'