Next row value without Window functions - mysql

I have the following table of page statuses updates. Every row is a status change:
CREATE TABLE `t1` (
`page_id` int(11) NOT NULL,
`country` varchar(2) COLLATE utf8mb4_unicode_ci NOT NULL,
`status` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`date` datetime DEFAULT NULL,
PRIMARY KEY (`page_id`,`country`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1 (page_id, country, status, date)
VALUES (1, 'de', 'deactivated', '2018-09-28 14:52:11' ) ,
(1, 'de', 'activated', '2018-09-28 14:54:18' ) ,
(1, 'de', 'deactivated', '2018-09-28 14:60:12' ) ,
(1, 'de', 'moderated', '2018-09-28 14:54:12' ) ,
(2, 'es', 'deactivated', '2018-09-28 14:52:01' ) ,
(2, 'es', 'activated', '2018-09-28 14:52:07' ) ,
(2, 'es', 'deactivated', '2018-09-28 14:52:11' )
I want to see the table in such a format, so that every row shows since when till when a page had a particular status:
CREATE TABLE `t2` (
`page_id` int(11) NOT NULL,
`country` varchar(2) COLLATE utf8mb4_unicode_ci NOT NULL,
`status` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`date_from` datetime DEFAULT NULL,
`date_to` datetime DEFAULT NULL,
PRIMARY KEY (`page_id`,`country`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t2 (page_id, country, status, date_from, date_to)
VALUES (1, 'de', 'deactivated', '2018-09-28 14:52:11', '2018-09-28 14:54:12' ) ,
(1, 'de', 'moderated', '2018-09-28 14:54:12', '2018-09-28 14:54:18' ) ,
(1, 'de', 'activated', '2018-09-28 14:54:18','2018-09-28 14:60:12' ) ,
(1, 'de', 'deactivated', '2018-09-28 14:60:12','2018-01-25 14:60:12' ) ,
(2, 'es', 'deactivated', '2018-09-28 14:52:01','2018-09-28 14:52:07' ) ,
(2, 'es', 'activated', '2018-09-28 14:52:07', '2018-09-28 14:52:11' ) ,
(2, 'es', 'deactivated', '2018-09-28 14:52:11','2018-01-25 14:52:11' ) ;
The problem is that we are still using MySQL 5.7 and haven't upgraded to mysql8 with cte and window functions, which would easily solve the problem:
SELECT
country,
page_id,
status,
date as date_from,
COALESCE(MIN(date) OVER(PARTITION BY country, page_id ORDER BY date DESC
ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING),
TIMESTAMPADD(day, 1, current_timestamp())) as date_to
FROM t1 ;
I assume there should be a trick with self join but can't figure out how exactly!

In MySQL 8+, you would normally use LEAD() for this, rather than your complicated expression.
In earlier versions, you can use a correlated subquery:
select t1.*,
(select tt1.date
from t1 tt1
where tt1.country = t1.country and tt1.page_id = t1.page_id and
tt1.date > t1.date
order by tt1.date asc
limit 1
) as date_to
from t1;
This uses NULL for the date_to values at the end of each sequence. That makes more sense to me.
If you want to repeat the date_from instead (which I don't recommend), then you can do:
select t1.*,
(select min(tt1.date_from, t1.date_from)
from t1 tt1
where tt1.page_id = t1.page_id and
tt1.date_from > t1.date_from
) as date_to
from t1;

Related

multiple sum in my sql

Trying to get result in single query however unable to get correct sum of order stock, it display sum twice if waste item exits.
Please see below query
SELECT DISTINCT cd.id,i.*,c.category_name, SUM(CASE WHEN s.stock_type = 'f' THEN s.stock END) AS instock, SUM(CASE WHEN s.stock_type = 'w' THEN s.stock END) AS weststock, SUM(cd.qty) AS orderstock, IF(SUM(CASE WHEN s.stock_type = 'f' THEN s.stock END) IS NULL,0, SUM(CASE WHEN s.stock_type = 'f' THEN s.stock END)) - IF(SUM(CASE WHEN s.stock_type = 'w' THEN s.stock END) IS NULL,0, SUM(CASE WHEN s.stock_type = 'w' THEN s.stock END)) - IF(SUM(cd.qty) IS NULL,0, SUM(cd.qty)) AS total
FROM item AS i
JOIN categories AS c ON i.category_id = c.id
LEFT JOIN stock AS s ON i.id=s.item_id
LEFT JOIN manage_godown AS mg ON mg.id = s.godown_id
LEFT JOIN cart_detail AS cd ON i.id=cd.item_id AND cd.cart_id IN (
SELECT ca.id
FROM cart AS ca
WHERE ca.status ='o')
WHERE mg.id=8
GROUP BY i.id
ORDER BY i.id DESC
Table Structure
----------------------
--
-- Table structure for table `admin`
--
CREATE TABLE IF NOT EXISTS `admin` (
`id` double NOT NULL AUTO_INCREMENT,
`email` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`mobile_no` varchar(50) NOT NULL,
`password` varchar(255) NOT NULL,
`status` enum('a','d') NOT NULL COMMENT 'a= active,d=deactive',
`type` enum('a','m') NOT NULL DEFAULT 'm',
`last_login` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`change_password_key` text NOT NULL,
`isAsigned` enum('y','n') NOT NULL DEFAULT 'n',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=12 ;
-- --------------------------------------------------------
--
-- Table structure for table `area`
--
CREATE TABLE IF NOT EXISTS `area` (
`id` double NOT NULL AUTO_INCREMENT,
`area_name` varchar(255) NOT NULL,
`area_status` enum('a','d') NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;
-- --------------------------------------------------------
--
-- Table structure for table `cart`
--
CREATE TABLE IF NOT EXISTS `cart` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` double NOT NULL,
`date` datetime NOT NULL,
`status` enum('c','o') NOT NULL DEFAULT 'c',
`ship_status` enum('y','n') NOT NULL DEFAULT 'n',
`shiping_address` text NOT NULL,
`order_date` datetime NOT NULL,
`area_id` int(11) NOT NULL,
`user_name` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`contact_number` varchar(255) NOT NULL,
`address` text NOT NULL,
`shipping_name` varchar(255) NOT NULL,
`order_delivery_status` enum('p','d','c') NOT NULL COMMENT 'p=pending,d=deliverd,c=cancle',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;
-- --------------------------------------------------------
--
-- Table structure for table `cart_detail`
--
CREATE TABLE IF NOT EXISTS `cart_detail` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`cart_id` int(11) NOT NULL,
`item_id` int(11) NOT NULL,
`qty` double NOT NULL,
`amount` double NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=13 ;
-- --------------------------------------------------------
--
-- Table structure for table `categories`
--
CREATE TABLE IF NOT EXISTS `categories` (
`id` double NOT NULL AUTO_INCREMENT,
`category_name` varchar(255) NOT NULL,
`isDefault` enum('y','n') NOT NULL DEFAULT 'n',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
-- --------------------------------------------------------
--
-- Table structure for table `item`
--
CREATE TABLE IF NOT EXISTS `item` (
`id` double NOT NULL AUTO_INCREMENT,
`category_id` double NOT NULL,
`item_name` varchar(255) NOT NULL,
`item_image` text NOT NULL,
`price` double NOT NULL,
`isPopular` enum('Y','N') NOT NULL,
`item_status` enum('a','d') NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=34 ;
-- --------------------------------------------------------
--
-- Table structure for table `manage_godown`
--
CREATE TABLE IF NOT EXISTS `manage_godown` (
`id` double NOT NULL AUTO_INCREMENT,
`godown_name` varchar(150) NOT NULL,
`address` text NOT NULL,
`contact_number` varchar(50) NOT NULL,
`contact_person` varchar(255) NOT NULL,
`area_id` text NOT NULL,
`user_id` varchar(100) NOT NULL,
`godown_status` enum('a','d') NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
--
-- Dumping data for table `area`
--
INSERT INTO `area` (`id`, `area_name`, `area_status`) VALUES
(1, 'C.G. Road', 'a'),
(2, 'S.G. Highway', 'a'),
(3, 'Bopal', 'a'),
(4, 'New Wadaj', 'a'),
(5, 'Ranip', 'a'),
(6, 'Bapunagar', 'a'),
(7, 'Gurukul RD', 'a'),
(8, 'Ghatlodia', 'a'),
(9, 'Nehru Nagar', 'a'),
(10, 'Old Wadaj', 'a'),
(11, 'Paldi', 'a'),
(12, 'NaranPura', 'a'),
(13, 'Anand Nagar', 'd'),
(14, 'Shahpur', 'a');
--
-- Dumping data for table `cart`
--
INSERT INTO `cart` (`id`, `user_id`, `date`, `status`, `ship_status`, `shiping_address`, `order_date`, `area_id`, `user_name`, `email`, `contact_number`, `address`, `shipping_name`, `order_delivery_status`) VALUES
(1, 1, '2015-03-09 20:30:36', 'o', 'n', 'roam', '2015-03-09 21:14:45', 7, 'account1', 'account1#superrito.com', '123456', 'roam', 'account1', 'p'),
(2, 1425936687, '2015-03-09 21:31:27', 'c', 'n', '', '0000-00-00 00:00:00', 0, '', '', '', '', '', 'p'),
(3, 1426012764, '2015-03-10 18:39:24', 'c', 'n', '', '0000-00-00 00:00:00', 0, '', '', '', '', '', 'p');
--
-- Dumping data for table `cart_detail`
--
INSERT INTO `cart_detail` (`id`, `cart_id`, `item_id`, `qty`, `amount`) VALUES
(1, 1, 1, 3, 80),
(2, 2, 1, 0.25, 80),
(3, 3, 1, 0.5, 80),
(4, 3, 2, 0.25, 45);
--
-- Dumping data for table `categories`
--
INSERT INTO `categories` (`id`, `category_name`, `isDefault`) VALUES
(1, 'vegitables', 'y'),
(2, 'fruits', 'y');
--
-- Dumping data for table `item`
--
INSERT INTO `item` (`id`, `category_id`, `item_name`, `item_image`, `price`, `isPopular`, `item_status`) VALUES
(1, 1, 'Tomato', '210fc803a958749e7b2a55c32c744f13.png', 80, 'Y', 'a'),
(2, 1, 'Potato', 'c244be2eab10bc46465d5b36448ba68b.jpg', 45, 'N', 'a'),
(3, 2, 'Cabbige', '05423c579cc99709923273c5222c4661.jpg', 120, 'Y', 'a');
--
-- Dumping data for table `manage_godown`
--
INSERT INTO `manage_godown` (`id`, `godown_name`, `address`, `contact_number`, `contact_person`, `area_id`, `user_id`, `godown_status`) VALUES
(8, 'Gurukul', 'Gurukul RD', '12457894', 'Salim', '7', '16', 'a'),
(9, 'Godown2', 'Godown2', '123456798', 'Samln', '12', '17', 'a');
--
-- Dumping data for table `user`
--
INSERT INTO `user` (`id`, `email`, `user_name`, `address`, `shiping_address`, `mobile_no`, `password`, `isVeryfied`, `created_date`, `email_verification`, `change_password_key`, `area_id`, `pincode`, `user_status`) VALUES
(1, 'account1#superrito.com', 'account1', 'roam', 'roam', '123456', '4b111bc4e9e96cd1d18d0359fdb94629', 'n', '2015-03-09 08:53:00', '', '', 0, 0, 'a'),
(2, 'tripathi_hhh#yahoo.com', 'hitesh tripathi', '', '', '9033913397', 'e10adc3949ba59abbe56e057f20f883e', 'n', '2015-03-10 06:31:23', '17a962a2eee74445747a3e194da6c556', 'be45b6a9362c65217ec88821b648a67f', 0, 0, 'a');
I placed order of Tomato 3KG Only however i added wasted item 3 times so sum of above query gives result 12KG in orderstock
Using COALESCE instead of IF ... IS NULL makes it a lot more readable... I'm waiting on your answer to #EdwinKrause's question to help with the rest.
SELECT DISTINCT cd.id,i.*,c.category_name,
SUM(CASE WHEN s.stock_type = 'f' THEN s.stock END) AS instock,
SUM(CASE WHEN s.stock_type = 'w' THEN s.stock END) AS weststock,
SUM(cd.qty) AS orderstock,
COALESCE( SUM(CASE WHEN s.stock_type = 'f' THEN s.stock END), 0) -
COALESCE(SUM(CASE WHEN s.stock_type = 'w' THEN s.stock END), 0) -
COALESCE(SUM(cd.qty), 0) AS total
FROM item AS i
JOIN categories AS c ON i.category_id = c.id
LEFT JOIN stock AS s ON i.id=s.item_id
LEFT JOIN manage_godown AS mg ON mg.id = s.godown_id
LEFT JOIN cart_detail AS cd ON i.id=cd.item_id AND cd.cart_id IN (
SELECT ca.id
FROM cart AS ca
WHERE ca.status ='o')
WHERE mg.id=2
GROUP BY i.id
ORDER BY i.id DESC

MySQL - Correct approach event counting

I want to list users which have a particular event count but I'm confused on which approach to take.
This is the database table:
CREATE TABLE `event` (
`event_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`visitor_id` int(11) DEFAULT NULL,
`key` varchar(200) DEFAULT NULL,
`value` text,
`label` varchar(200) DEFAULT '',
`datetime` datetime DEFAULT NULL,
PRIMARY KEY (`event_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
INSERT INTO `event` (`event_id`, `visitor_id`, `key`, `value`, `label`, `datetime`)
VALUES
(1, 1, 'LOGIN', NULL, '', NULL),
(2, 2, 'LOGIN', NULL, '', NULL),
(3, 1, 'VIEW_PAGE', 'HOTEL', '', NULL),
(4, 2, 'VIEW_PAGE', 'HOTEL', '', NULL),
(5, 1, 'PURCHASE_HOTEL', NULL, '', NULL);
CREATE TABLE `visitor` (
`visitor_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`datetime` datetime DEFAULT NULL,
PRIMARY KEY (`visitor_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
INSERT INTO `visitor` (`visitor_id`, `datetime`)
VALUES
(1, NULL),
(2, NULL);
and this is my approach:
SELECT DISTINCT
t1.`visitor_id`
FROM
`visitor` t1
JOIN `event` t2 on t1.visitor_id = t2.visitor_id AND t2.`key` = 'LOGIN'
JOIN `event` t3 on t1.visitor_id = t3.visitor_id AND t3.`key` = 'VIEW_PAGE' AND t3.`value` = 'HOTEL'
WHERE ( SELECT COUNT(*) FROM `event` WHERE `event`.`key` = 'PURCHASE_HOTEL' ) > 0
this should only list visitor 1 but it does actually list visitor 2 too which does not have the PURCHASE_HOTEL event.
As you can imagine, there will be more "rules" like all the JOIN events for each particular case. Can we correct and improve this somehow?
BONUS:
What is the name of this approach?
I think this is a "set-within-sets" query. I like using aggregation with a having clause for this type of query. The following checks the three conditions you are looking for:
select visitor_id
from event e
group by visitor_id
having sum(e.key = 'LOGIN') > 0 and
sum(e.key = 'VIEW_PAGE' and e.value = 'HOTEL') > 0 and
sum(e.key = 'PURCHASE_HOTEL') > 0;
The first condition in the having clause counts the number of LOGIN records and is true when at least one is found. (If you want exactly one, change > 0 to = 0.)
The second condition checks the viewing of the hotel page.
The third counts the number of hotel purchases.

How to get multiple rows with order by and limit

I have a mysql table, the structure of that table is as follows:
CREATE TABLE IF NOT EXISTS `salary_log` (`salary_id` int(11) NOT NULL AUTO_INCREMENT, `salary` int(11) NOT NULL, `name` varchar(200) NOT NULL, `create_date` date NOT NULL, PRIMARY KEY (`salary_id`)) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
INSERT INTO `salary_log` (`salary_id`, `salary`, `name`, `create_date`) VALUES (1, 5000, 'a', '2013-05-02'), (2, 8000, 'a', '2013-05-08'), (3, 4500, 'b', '2013-05-10'), (4,6000, 'c', '2013-05-08'), (5, 8000, 'b', '2013-05-17');
From the above table i need to fetch the latest records of each employee. I tried below query :
SELECT * FROM `salary_log` GROUP BY `name` ORDER BY `create_date` DESC;
But it returns be wrong data. Can someone please help me in this.
Try this:
SELECT s1.*
FROM `salary_log` AS s1
INNER JOIN
(
SELECT name, MAX(create_date) AS maxDate
FROM salary_log
GROUP BY `name`
) AS s2 ON s1.name = s2.name
AND s1.create_date = s2.maxDate;

between two dates i need to display "today result"

Between two dates I need to display "today result".
CREATE TABLE IF NOT EXISTS `maf_game_stats` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userid` varchar(100) NOT NULL,
`sessionid` varchar(100) NOT NULL,
`gid` int(11) NOT NULL,
`qid` int(11) NOT NULL,
`result` varchar(50) NOT NULL,
`accesstime` datetime NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `maf_game_stats` (`id`, `userid`, `sessionid`, `gid`, `qid`, `result`, `accesstime`) VALUES
(1, 'FYEJHQWSHXMV263', '35sd797n57plmreb5ecssuhhh2', 1, 5, '0', '2013-03-30 13:34:03'),
(2, 'FYEJHQWSHXMV263', '35sd797n57plmreb5ecssuhhh2', 1, 1, '0', '2013-03-30 13:34:07'),
(3, 'FYEJHQWSHXMV263', '35sd797n57plmreb5ecssuhhh2', 1, 10, '1', '2013-03-30 13:34:12'),
(4, 'FYEJHQWSHXMV263', '35sd797n57plmreb5ecssuhhh2', 1, 2, '1', '2013-03-30 13:34:17'),
(5, 'FYEJHQWSHXMV263', '35sd797n57plmreb5ecssuhhh2', 1, 16, '1', '2013-03-30 13:34:23');
My query is:
SELECT a.gid, a.qid, SUM(a.result = 1) correct, SUM(a.result = 0) incorrect FROM maf_game_stats a
INNER JOIN
(
SELECT USERID, gid, QID, MIN(ACCESSTIME) min_date
FROM maf_game_stats
GROUP BY USERID, gid, qid
) b ON a.USERID = b.USERID AND
a.gid = b.gid AND
a.qid = b.qid AND
a.ACCESSTIME = b.min_date
WHERE a.gid ='1'
AND a.ACCESSTIME BETWEEN CAST('2013-03-29' AS DATETIME) AND CAST('2013-03-30' AS DATETIME)
GROUP BY a.gid, a.qid
If I understand your question correctly, I think you just need to filter by current date:
WHERE a.gid ='1'
AND DATE(a.ACCESSTIME) = CURDATE()
GROUP BY a.gid, a.qid;
or you can use this, that could be faster because is able to make use of an index:
WHERE a.gid ='1'
AND a.ACCESSTIME >= CURDATE() AND a.ACCESSTIME < CURDATE() + INTERVAL 1 DAY
GROUP BY a.gid, a.qid;

MYSQL returns empty result set?

I have a table with the following structure:
CREATE TABLE IF NOT EXISTS `user_settings` (
`user_ID` smallint(6) unsigned NOT NULL,
`item` varchar(50) NOT NULL,
`value` text,
`app_ID` tinyint(4) NOT NULL DEFAULT '0',
KEY `user_ID` (`user_ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
# With these data
INSERT INTO `user_settings` (`user_ID`, `item`, `value`, `app_ID`) VALUES
(3, 'statusTrainer_ID', '0', 0),
(3, 'enabledApp', '1', 0),
(3, 'enabled_office_ID', '1', 1),
(3, 'enabled_office_ID', '2', 1),
(4, 'statusTrainer_ID', '1', 0),
(1, 'enabledApp', '1', 0),
(1, 'salesCode_ID_add', '1', 1),
(1, 'salesCode_ID_add', '2', 1),
(1, 'salesCode_ID_add', '3', 1),
(1, 'salesCode_ID_add', '20', 1),
(1, 'salesCode_ID_process', '2', 1),
(1, 'salesCode_ID_process', '3', 1),
(1, 'salesCode_ID_process', '20', 1);
I would like to run this query for data filtering
SELECT `user_ID`
FROM (`user_settings`)
WHERE (
`item` = 'enabledApp'
AND
`value` IN ('1')
)
AND
(
`item` = 'statusTrainer_ID'
AND
`value` IN ('0')
)
I am expecting '3' as result but MYSQL returns an empty set. why?? thanks for answer.
UPDATED
I solved it by subquery
SELECT distinct `user_ID`
FROM (`user_settings`)
WHERE user_ID IN(
SELECT `user_ID`
FROM (`user_settings`)
WHERE (
item = 'enabledApp'
AND
value IN (1)
))
AND user_ID IN(SELECT `user_ID`
FROM (`user_settings`)
WHERE (
item = 'statusTrainer_ID'
AND
value IN (0)
))
Demo
but it's little complicated...is there some cleaner solution????
You need an or:
SELECT `user_ID`
FROM (`user_settings`)
WHERE ( `item` = 'enabledApp' AND `value` IN ('1') ) OR
( `item` = 'statusTrainer_ID' AND `value` IN ('0') )
It returned the empty set because item cannot be both "enabledApp" and "statusTrainer_ID".
Oh, you are trying to get user ids that have both. Do it this way:
SELECT `user_ID`
FROM `user_settings`
GROUP BY `user_ID`
HAVING max(case when `item` = 'enabledApp' AND `value` IN ('1') then 1 else 0 end) = 1 and
max(case when `item` = 'statusTrainer_ID' AND `value` IN ('0') then 1 else 0 end) = 1
Demo