I have a problem with my update query. I need to change notify_admin from 0 to 1 only for last users row if action_type = abuse. (Result should be rows with id=9 and id=13)
I'm trying something like that:
UPDATE user_log SET notify_admin = 1
WHERE id IN (
SELECT DISTINCT user_id FROM (SELECT user_id FROM user_log) as UNIKALNE
) AND action_type LIKE 'abuse'
Unfortunately it update only 1 row (id=3).
Here is my table:
CREATE TABLE IF NOT EXISTS `user_log` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`action_type` enum('login','logout','abuse') CHARACTER SET latin1 NOT NULL,
`notify_admin` tinyint(1) NOT NULL DEFAULT '0',
`saved` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=15;
INSERT INTO `user_log` (`id`, `user_id`, `action_type`, `notify_admin`, `saved`) VALUES
(1, 1, 'login', 0, '2015-11-02 12:13:14'),
(2, 1, 'logout', 0, '2015-11-02 13:12:11'),
(3, 1, 'abuse', 0, '2016-01-03 14:10:02'),
(4, 2, 'abuse', 0, '2016-01-04 17:47:03'),
(5, 2, 'login', 0, '2016-01-04 18:11:55'),
(6, 1, 'abuse', 0, '2016-01-04 18:23:57'),
(7, 1, 'abuse', 0, '2016-01-04 18:24:23'),
(8, 2, 'logout', 0, '2016-01-04 18:25:24'),
(9, 1, 'abuse', 0, '2016-01-04 18:25:32'),
(10, 1, 'login', 0, '2016-01-05 21:02:59'),
(11, 3, 'login', 0, '2016-01-05 21:28:43'),
(12, 3, 'logout', 0, '2016-01-05 21:52:01'),
(13, 2, 'abuse', 0, '2016-01-05 22:00:35'),
(14, 1, 'logout', 0, '2016-01-05 22:12:09');
You need to first get the most recent saved value per user and then update the column.
UPDATE user_log
JOIN
(
select id from user_log JOIN (
select user_id,max(saved) max_saved
from user_log
where action_type="abuse"
group by user_id
) t
ON t.user_id = user_log.user_id AND t.max_saved = user_log.saved
) t2
ON user_log.id = t2.id
SET notify_admin = 1
Here you are checking User_id from table with distinct so it will give 1,2,3 only and then comparing with abuse action type so it will update row 3 which matches.
replace User_id with id if you want to update for all rows
UPDATE user_log SET notify_admin = 1 WHERE id IN (SELECT DISTINCT id FROM (SELECT id FROM user_log) as UNIKALNE) AND action_type LIKE 'abuse'
Related
How can I get the total of hectar and total amount in one row rather than looping it in many rows with php and mysql?
I am using the following sql table:
-- Database: officedb
CREATE TABLE IF NOT EXISTS `agricollectcropedata` (
`kebele` varchar(50) NOT NULL,
`croptype` varchar(40) NOT NULL,
`hektar` int(40) NOT NULL,
`amount` int(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `agricollectcropedata` (`kebele`, `croptype`, `hektar`, `amount`) VALUES
('b', 'wheet', 2, 12),
('a', 'wheet', 1, 5),
('a', 'wheet', 2, 6),
('a', 'wheet', 3, 12),
('a', 'wheet', 0, 0),
('a', 'wheet', 0, 0);
SELECT
sum(hektar) as hektar,
sum(amount) as amount
FROM
agricollectcropedata
I have like this task currently:
CREATE TABLE IF NOT EXISTS `files` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`id_user` INT(11) DEFAULT NULL,
`flag_converted` TINYINT(1) DEFAULT '0' COMMENT '0 - not converted, 1 - converted',
`flag_error` TINYINT(1) DEFAULT '0' COMMENT '0 - no errors, 1 - error occurred',
`rating` INT(11) UNSIGNED DEFAULT NULL COMMENT '1-5',
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `files` (`id_user`, `flag_converted`, `flag_error`, `rating`) VALUES (NULL, 0, 0, NULL);
INSERT INTO `files` (`id_user`, `flag_converted`, `flag_error`, `rating`) VALUES (NULL, 1, 0, 5);
INSERT INTO `files` (`id_user`, `flag_converted`, `flag_error`, `rating`) VALUES (NULL, 1, 0, 5);
INSERT INTO `files` (`id_user`, `flag_converted`, `flag_error`, `rating`) VALUES (NULL, 0, 1, 2);
INSERT INTO `files` (`id_user`, `flag_converted`, `flag_error`, `rating`) VALUES (1, 0, 0, NULL);
INSERT INTO `files` (`id_user`, `flag_converted`, `flag_error`, `rating`) VALUES (1, 1, 0, 5);
INSERT INTO `files` (`id_user`, `flag_converted`, `flag_error`, `rating`) VALUES (1, 0, 1, 2);
INSERT INTO `files` (`id_user`, `flag_converted`, `flag_error`, `rating`) VALUES (2, 0, 0, NULL);
INSERT INTO `files` (`id_user`, `flag_converted`, `flag_error`, `rating`) VALUES (3, 1, 0, 5);
INSERT INTO `files` (`id_user`, `flag_converted`, `flag_error`, `rating`) VALUES (3, 1, 0, 5);
INSERT INTO `files` (`id_user`, `flag_converted`, `flag_error`, `rating`) VALUES (4, 0, 1, 2);
INSERT INTO `files` (`id_user`, `flag_converted`, `flag_error`, `rating`) VALUES (4, 0, 1, 2);
INSERT INTO `files` (`id_user`, `flag_converted`, `flag_error`, `rating`) VALUES (4, 0, 1, 2);
INSERT INTO `files` (`id_user`, `flag_converted`, `flag_error`, `rating`) VALUES (4, 0, 1, 2);
(sql fiddle: http://sqlfiddle.com/#!9/431723/49/0)
It is necessary to form a SQL query to the files table which displays the number of files based on the success of the conversion and the presence of a good rating from registered / unregistered users.
Output format:
user_type ('Visitor' if id_user = NULL; 'User' otherwise),
flag_converted,
is_best_rating ('1' - if rating = 5; '0' otherwise),
count (number of records).
The results must be sorted by count and is_best_rating in reverse order. And display only groups where count is greater than 1.
My current query:
SELECT id, flag_converted, CASE
WHEN rating = 5 THEN '1'
ELSE '0'
END AS is_best_rating, CASE
WHEN id_user IS NULL THEN 'Visitor'
ELSE 'User'
END AS user_type from files
In my task says:
The results must be sorted by count
Where I can get value of count correctly in this case and sort?
If I understand correctly, according to the task requirement, you'll need to return 4 columns user_type,flag_converted,is_best_rating and count with specific CASE or IF condition on the id_user and rating. Your current query seems to met most of those conditions but let's try to follow the tasks requirement. First, change the columns in SELECT to:
SELECT
CASE
WHEN id_user IS NULL THEN 'Visitor'
ELSE 'User'
END AS user_type,
flag_converted,
CASE
WHEN rating = 5 THEN '1'
ELSE '0'
END AS is_best_rating,
COUNT(*) AS count
...
Since we're adding COUNT() which is an aggregation, we need to modify the query with a GROUP BY. So all three other columns in SELECT except for count must be added to the GROUP BY:
SELECT
CASE
WHEN id_user IS NULL THEN 'Visitor'
ELSE 'User'
END AS user_type,
flag_converted,
CASE
WHEN rating = 5 THEN '1'
ELSE '0'
END AS is_best_rating, COUNT(*) AS count
from files
GROUP BY user_type, flag_converted, is_best_rating
And for this condition "The results must be sorted by count and is_best_rating in reverse order. And display only groups where count is greater than 1.". You probably can do something like this:
SELECT
CASE
WHEN id_user IS NULL THEN 'Visitor'
ELSE 'User'
END AS user_type,
flag_converted,
CASE
WHEN rating = 5 THEN '1'
ELSE '0'
END AS is_best_rating, COUNT(*) AS count
from files
GROUP BY user_type, flag_converted, is_best_rating
HAVING count > 1
ORDER BY count DESC, is_best_rating DESC;
I think reverse order means the largest value first so ORDER BY xxx DESC is making sure of that. As for HAVING count > 1 its self-explanatory but with or without it, I don't see any count=1; kind of make me think that I might've missed something.
Anyway here's your updated fiddle
I have a query that grabs tags for a list of articles and limits it to under 5 tags per article. This works pretty well.
Here's the query:
SET #rank=null, #val=null;
SELECT * FROM (
SELECT r.article_id, c.`category_name`, c.`category_id`,
#rank := IF( #val = r.article_id, #rank +1, 1 ) AS rank,
#val := r.article_id
FROM `article_category_reference` r
INNER JOIN `articles_categorys` c ON c.category_id = r.category_id
WHERE r.article_id
IN ( 1,2 )
ORDER BY r.`article_id` ASC
) AS a
WHERE rank < 5
However, I have specific tags I want to show up first which have a column of "show_first" 0/1 and I want them included first and be counted.
I've tried doing:
ORDER BY CASE WHEN (c.`show_first` = 1) THEN 0 ELSE 1 END, r.`article_id` ASC
Which breaks the rank counting, so all tags end up showing.
Any pointers would be appreciated.
The tables:
CREATE TABLE `article_category_reference` (
`ref_id` int(11) NOT NULL,
`article_id` int(11) NOT NULL,
`category_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Indexes for table `article_category_reference`
--
ALTER TABLE `article_category_reference`
ADD PRIMARY KEY (`ref_id`),
ADD KEY `category_id` (`category_id`),
ADD KEY `article_id` (`article_id`);
CREATE TABLE `articles_categorys` (
`category_id` int(11) NOT NULL,
`category_name` varchar(32) CHARACTER SET utf8 NOT NULL,
`quick_nav` tinyint(1) NOT NULL DEFAULT '0',
`is_genre` tinyint(1) NOT NULL DEFAULT '0',
`show_first` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
--
-- Indexes for table `articles_categorys`
--
ALTER TABLE `articles_categorys`
ADD PRIMARY KEY (`category_id`);
-- And some data:
INSERT INTO `articles_categorys` (`category_id`, `category_name`, `quick_nav`, `is_genre`, `show_first`) VALUES
(1, 'one', 1, 0, 0),
(2, 'two', 1, 0, 0),
(3, 'three', 1, 0, 0),
(4, 'four', 0, 0, 0),
(5, 'five', 0, 0, 0),
(6, 'six', 0, 0, 0),
(7, 'seven', 0, 0, 1),
(8, 'eight', 0, 0, 1);
INSERT INTO `article_category_reference` (`ref_id`, `article_id`, `category_id`) VALUES
(1, 1, 1),
(2, 1, 2),
(3, 1, 3),
(4, 1, 4),
(5, 1, 5),
(6, 1, 6),
(7, 1, 7),
(8, 1, 8),
(9, 2, 1),
(10, 2, 2),
(11, 2, 3),
(12, 2, 4),
(13, 2, 5),
(14, 2, 6),
(15, 2, 7),
(16, 2, 8);
Fiddle of how it works: http://sqlfiddle.com/#!9/1de99/1/0
Fiddle of it not working with me wanting some to always show first: http://sqlfiddle.com/#!9/0d36b7/1 (adding in a second group seems to break the ranking system)
Your issue is not in the where condition, it's about the ranking that you are creating.
As you will see in my answer, I have created one inner query which will get that record in specific order and apply accurate ranking.
If you check your inner query, it's shows that all rows have the same rank and that is due to that ordering issue.
So I have added the ORDER BY clause in innermost query, and then filtered out records which have rank1 less than 5.
SET #rank1=null, #val=null;
SELECT * FROM (
SELECT a.article_id, a.`category_name`, a.`category_id`,
#rank1 := IF( #val = a.article_id, #rank1 +1, 1 ) AS rank1,
#val := a.article_id
FROM (
SELECT r.article_id, c.`category_name`, c.`category_id`
FROM `article_category_reference` r
INNER JOIN `articles_categorys` c ON c.category_id = r.category_id
GROUP BY r.article_id, c.`category_name`, c.`category_id`
ORDER BY r.`article_id`,CASE WHEN (c.`show_first` = 1) THEN 0 ELSE 1 END ASC
) AS a
) Z
WHERE Z.rank1 < 5;
You can check here.
I am working with inventory data that tells me the current inventory level every minute and stores it in the DB.
I want to find each instance where item_count fell to 0, take that row with timestamp, and then join it to the next row where the item_count rose above 0. This will then tell me how long that product was out of stock.
I came up with the following, but it doesn't return anything.
SELECT `inventories`.* from `inventories` inner join
(SELECT id, item_count, pusher_id, created_at as in_stock_at
FROM inventories
GROUP BY pusher_id) inv2
ON `inventories`.`created_at` < `inv2`.`in_stock_at`
AND `inv2`.`item_count` > `inventories`.`item_count`
AND `inventories`.`pusher_id` = `inv2`.`pusher_id`
WHERE `inventories`.`item_count` <= 0
AND `inventories`.`product_id`=9
Structure::
CREATE TABLE IF NOT EXISTS `inventories` (
`id` int(10) unsigned NOT NULL,
`client_id` int(10) unsigned NOT NULL,
`pusher_id` int(10) unsigned NOT NULL,
`product_id` int(10) unsigned NOT NULL,
`reader_id` int(10) unsigned NOT NULL,
`tags_blocked` double(6,2) NOT NULL,
`item_count` double(6,2) NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=2881 ;
Data::
INSERT INTO `inventories` (`id`, `client_id`, `pusher_id`, `product_id`, `reader_id`, `tags_blocked`, `item_count`, `active`, `created_at`, `updated_at`, `deleted_at`) VALUES
(1, 1, 1, 9, 1, 0.00, 0.00, 1, '2015-10-22 04:45:47', '2015-10-23 04:45:47', NULL),
(2, 1, 1, 9, 1, 0.00, 0.00, 1, '2015-10-22 04:55:47', '2015-10-23 04:45:47', NULL),
(3, 1, 1, 9, 1, 0.00, 0.00, 1, '2015-10-22 05:05:47', '2015-10-23 04:45:47', NULL),
...
(10, 1, 1, 9, 1, 0.00, 0.00, 1, '2015-10-22 06:15:47', '2015-10-23 04:45:47', NULL),
(11, 1, 1, 9, 1, 10.00, 10.00, 1, '2015-10-22 06:25:47', '2015-10-23 04:45:47', NULL),
(12, 1, 1, 9, 1, 9.00, 9.00, 1, '2015-10-22 06:35:47', '2015-10-23 04:45:47', NULL),
(13, 1, 1, 9, 1, 8.00, 8.00, 1, '2015-10-22 06:45:47', '2015-10-23 04:45:47', NULL),
Desired Result::
Given the data above, I want to join row with ID 1 and row with ID 11.
1. Search the table for the first row with item_count=0, find a row (with same product_id and pusher_id) that has item_count > 0 and created_at > firstRow.created_at. and join them together.
Then, find the next instance of this occurrence.
I hope that clarifies the question.
Translating into SQL isn't that hard, but performance might be bad. This will get you the timestamp when the product was back in stock:
SELECT inv.*,
( SELECT MIN(`inv2`.`in_stock_at`)
FROM inventories AS inv2
WHERE inv2.`product_id` = inv.`product_id` -- same product
AND inv2.`pusher_id` = `inv`.`pusher_id` -- same pusher
AND `inv2`.`created_at` > inv.`created_at` -- later timestamp
AND `inv2`.`item_count` > 0 -- in stock
) AS inStockAgain_at
from `inventories` AS inv
WHERE inv.`item_count` <= 0 -- out of stock
-- AND inv.`product_id`=9
Edit:
Removing consecutive rows with zero stock is more complicated:
SELECT inv.*, dt.inStockAgain_at
FROM inventories AS inv
JOIN
(
SELECT product_id, pusher_id,
MIN(created_at) AS min_created_at,
inStockAgain_at
FROM
(
SELECT product_id, pusher_id, created_at,
( SELECT MIN(inv2.created_at)
FROM inventories AS inv2
WHERE inv2.product_id = inv.product_id -- same product
AND inv2.pusher_id = inv.pusher_id -- same pusher
AND inv2.created_at > inv.created_at -- later timestamp
AND inv2.item_count > 0 -- in stock
) AS inStockAgain_at
FROM inventories AS inv
WHERE inv.item_count <= 0
) AS dt
GROUP BY product_id, pusher_id, inStockAgain_at
) AS dt
ON inv.product_id = dt.product_id
AND inv.pusher_id = dt.pusher_id
AND inv.created_at = dt.min_created_at
See fiddle
I wish to divide values from the same column using mySQL.
is there a better way of doing this??? Do I really need 3 select statements?
SELECT (SELECT FixAM From Fixes WHERE Id = 1) / (SELECT FixAM From Fixes WHERE Id = 2)
My table structure is as follows:
CREATE TABLE IF NOT EXISTS `Fixes` (
`Id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'PK',
`CurrencyId` int(11) NOT NULL COMMENT 'FK',
`MetalId` int(11) NOT NULL COMMENT 'FK',
`FixAM` decimal(10,5) NOT NULL,
`FixPM` decimal(10,5) DEFAULT NULL,
`TimeStamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`Id`),
KEY `CurrencyId` (`CurrencyId`),
KEY `MetalId` (`MetalId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=13 ;
--
-- Dumping data for table `Fixes`
--
INSERT INTO `Fixes` (`Id`, `CurrencyId`, `MetalId`, `FixAM`, `FixPM`, `TimeStamp`) VALUES
(1, 1, 1, '1592.50000', '1586.25000', '2013-02-25 15:10:21'),
(2, 2, 1, '1051.84900', '1049.59300', '2013-02-25 15:10:21'),
(3, 3, 1, '1201.88700', '1194.10600', '2013-02-25 15:10:21'),
(4, 1, 2, '29.17000', NULL, '2013-02-25 13:54:02'),
(5, 2, 2, '19.27580', NULL, '2013-02-25 13:54:02'),
(6, 3, 2, '21.98190', NULL, '2013-02-25 13:54:02'),
(7, 1, 3, '1627.00000', '1620.00000', '2013-02-25 14:28:59'),
(8, 2, 3, '1074.65000', '1072.50000', '2013-02-25 14:28:59'),
(9, 3, 3, '1229.30000', '1218.95000', '2013-02-25 14:28:59'),
(10, 1, 4, '747.00000', '748.00000', '2013-02-25 14:28:59'),
(11, 2, 4, '493.40000', '495.20000', '2013-02-25 14:28:59'),
(12, 3, 4, '564.40000', '562.85000', '2013-02-25 14:28:59');
I think this is what you're looking for:
SELECT MetalId,
MAX(CASE WHEN CurrencyId = 1 THEN FixAM END) /
MAX(CASE WHEN CurrencyId = 2 THEN FixAM ELSE 1 END) Output
FROM Fixes
GROUP BY MetalId
This produces 1592.50000 / 1051.849000. If you want the opposite, swap the currency ids.
SQL Fiddle Demo
In case you don't have a CurrencyId = 2, I defaulted the dividing value to 1 so you wouldn't receive an error.