MySQL, using columns in another table for a CASE statement - mysql

I have two tables, an Items table that looks like this:
Items
Item Count
-----------------
1 20
2 24
3 49
And a table called ItemsConfig that looks like this:
ItemsConfig
ItemCountLow ItemCountHigh ItemCountStatus
------------------------------------------------
0 20 Low
21 50 Normal
51 100 Surplus
What I would like to do is build a query that will compare item counts in the Items table using the ItemCountLow and ItemCountHigh thresholds in the ItemsConfig table, to derive the ItemCountStatus.
Here are the create statements for each of these tables
Items table
CREATE TABLE IF NOT EXISTS `Items` (
`Item` int(11) NOT NULL,
`Count` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Dumping data for table `Items`
--
INSERT INTO `Items` (`Item`, `Count`) VALUES
(1, 20),
(2, 24),
(3, 49);
ItemsConfig table
CREATE TABLE IF NOT EXISTS `ItemsConfig` (
`ItemCountLow` int(11) NOT NULL,
`ItemCountHigh` int(11) NOT NULL,
`ItemCountStatus` varchar(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Dumping data for table `ItemsConfig`
--
INSERT INTO `ItemsConfig` (`ItemCountLow`, `ItemCountHigh`, `ItemCountStatus`) VALUES
(0, 20, 'Low'),
(21, 50, 'Normal'),
(51, 100, 'Surplus');
I'm trying to do something like this, but can't seem to figure it out.
SELECT item, count,
CASE
WHEN count > ItemsConfig.ItemCountLow AND count < ItemsConfig.ItemCountHigh
THEN ItemsConfig.ItemCountStatus
END as 'status'
FROM Items
Could someone please help?

How about:
SELECT i.item, i.count, ifnull(ic.ItemCountStatus,'Unknown') status
FROM items i
LEFT JOIN ItemsConfig ic on (i.count between ic.ItemCountLow AND ic.ItemCountHigh)

Related

Get Rows contains Pattern MYSQL

I have very simple table like below
CREATE TABLE `tbl_data` (
`id` int(11) NOT NULL,
`won` tinyint(4) NOT NULL,
`time` datetime NOT NULL DEFAULT current_timestamp()
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `tbl_data`
--
INSERT INTO `tbl_data` (`id`, `won`, `time`) VALUES
(1, 1, '2022-10-18 05:21:37'),
(2, 2, '2022-10-18 05:21:37'),
(5, 0, '2022-10-18 05:22:02'),
(6, 2, '2022-10-18 05:22:02'),
(7, 2, '2022-10-18 05:22:18'),
(8, 1, '2022-10-18 05:22:18');
--
-- Indexes for dumped tables
--
--
-- Indexes for table `tbl_data`
--
ALTER TABLE `tbl_data`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `tbl_data`
--
ALTER TABLE `tbl_data`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=9;
COMMIT;
What I am trying to achieve is select all rows which contains pattern of won like 0,2,2
so it will select id called 5,6,7 in above example.
if my pattern is like 1,2
it should select ids called 1 and 2 like this.
Database Fiddle is here
https://www.db-fiddle.com/#&togetherjs=RDJuie555L
I am finding solution from last hour but not able to achieve the goal. Let me know if any expert here can help me for same.
Thanks!
WITH cte AS (
SELECT id id0,
LEAD(id) OVER w id1,
LEAD(id,2) OVER w id2,
CONCAT_WS(',',
won,
LEAD(won) OVER w,
LEAD(won,2) OVER w) won_list
FROM tbl_data
WINDOW w AS (ORDER BY id)
)
SELECT tbl_data.*
FROM tbl_data
JOIN cte ON tbl_data.id IN (cte.id0, cte.id1, cte.id2)
WHERE cte.won_list = '0,2,2'
id
won
time
5
0
2022-10-18 05:22:02
6
2
2022-10-18 05:22:02
7
2
2022-10-18 05:22:18
fiddle
PS. If there exists more than one copy of needed pattern then the rows will mix. Add ORDER BY id clause to the outer query for to sort output rows and any of cte.idN (N=0..2) to its output list for to see separate pattern groups.

MySQL - GROUP results BY a single column alongside ORDER BY and LIMIT

I want to select a list of tasks from my database. The tasks have a category_id. I want to get a singlur task per category_id. So if I, for example, had 10 tasks that are linked to like 6 categories that would result in 6 results. The 6 results I want are determined by their id, the lowest id among the GROUP BY is the correct record for that GROUP. Also the maximum result set can be no larger than 20 ('LIMIT').
SELECT * FROM `task` WHERE `datetime`<NOW() `task_status_id`=1 GROUP BY `category_id` ORDER BY `id` ASC LIMIT 20
What is wrong with the above query, I got no clue, I'm also at a loss getting any google results for this.
ADDED LATER
http://sqlfiddle.com/#!9/fa39cf
CREATE TABLE `category` (
`id` int(10) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `category` (`id`) VALUES
(1),
(2),
(3);
CREATE TABLE `task` (
`id` int(10) UNSIGNED NOT NULL,
`category_id` int(10) UNSIGNED NOT NULL,
`task_status_id` int(10) UNSIGNED DEFAULT '1',
`datetime` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `task` (`id`, `category_id`, `task_status_id`, `datetime`) VALUES
(3, 2, 1, '2018-07-24 11:20:26'),
(4, 2, 1, '2018-07-24 11:20:26'),
(5, 3, 1, '2018-07-24 11:21:35'),
(6, 3, 1, '2018-07-24 11:21:35');
You can try first finding the smallest id for each category and then joining it with the task table to get the remaining details.
SELECT t.* FROM task t
JOIN (SELECT category_id, min(id) id from task group by category_id) tc
ON (t.id = tc.id)
LIMIT 20

mysql - grouping results taking the first in an ordered subquery

In mysql, I'm having trouble pulling a single row for each foreign_id based on the largest value. Strangely, different versions of mysql works (listed below)
id foreign_id value
---------------------
1 1 1000
2 1 2000
3 2 2000
4 2 1000
5 3 2000
I try to pull ids 2,3,5 not 1,3,5
CREATE TABLE `docs` (
`id` int(11) NOT NULL,
`foreign_id` int(6) DEFAULT NULL,
`value` int(8) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `docs`
ADD PRIMARY KEY (`id`),
ADD KEY `foreign_id_index` (`foreign_id`);
INSERT INTO `docs` (`id`, `foreign_id`, `value`) VALUES
(1, 1, 1000), (2, 1, 2000), (3, 2, 2000), (4, 2, 1000), (5, 3, 2000)
select
docs.id, docs.foreign_id, docs.value
FROM docs
INNER JOIN
(select id, max(value) from docs group by foreign_id) sub
ON sub.id = docs.id
# expected results are ids (2,3,5), not (1,3,5)
It's simpler than it looks
SELECT D.id, D.foreign_id, max_vals.max_val as value
FROM docs D
JOIN
(SELECT foreign_id, MAX(value) as max_val
FROM docs
GROUP BY foreign_id) max_vals
ON D.foreign_id=max_vals.foreign_id and D.value=max_vals.max_val
In this case, you need JOIN and not INNER JOIN
There is a difficult case where there are 2 or more foreign_ids with the same MAX value...

Joining table with min(amount) does not work

I have 3 tables, but data is only fetch from 2 tables.
I'm trying to get the lowest bids for selected items and display user name with the lowest bid.
Currently query works until when we display user name, it shows wrong user name, which does not match the bid.
Below is working example of structure and query.
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE `bid` (
`id` int(11) NOT NULL,
`amount` float NOT NULL,
`user_id` int(11) NOT NULL,
`item_id` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
INSERT INTO `bid` (`id`, `amount`, `user_id`, `item_id`) VALUES
(1, 9, 1, 1),
(2, 5, 2, 1),
(3, 4, 3, 1),
(4, 3, 4, 1),
(5, 4, 2, 2),
(6, 22, 5, 1);
-- --------------------------------------------------------
CREATE TABLE `item` (
`id` int(11) NOT NULL,
`name` varchar(100) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
INSERT INTO `item` (`id`, `name`) VALUES
(1, 'chair'),
(2, 'sofa'),
(3, 'table'),
(4, 'box');
-- --------------------------------------------------------
CREATE TABLE `user` (
`id` int(11) NOT NULL,
`name` varchar(100) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
INSERT INTO `user` (`id`, `name`) VALUES
(1, 'James'),
(2, 'Don'),
(3, 'Hipes'),
(4, 'Sam'),
(5, 'Zakam');
ALTER TABLE `bid`
ADD PRIMARY KEY (`id`);
ALTER TABLE `item`
ADD PRIMARY KEY (`id`);
ALTER TABLE `user`
ADD PRIMARY KEY (`id`);
ALTER TABLE `bid`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=7;
ALTER TABLE `item`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=5;
ALTER TABLE `user`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=5;
Query 1:
SELECT b.id, b.item_id, MIN(b.amount) as amount, b.user_id, p.name
FROM bid b
LEFT JOIN user p ON p.id = b.user_id
WHERE b.item_id in (1, 2)
GROUP BY b.item_id
ORDER BY b.amount, b.item_id
Results:
| id | item_id | amount | user_id | name |
|----|---------|--------|---------|-------|
| 5 | 2 | 4 | 2 | Don |
| 1 | 1 | 3 | 1 | James |
Explanation of query:
Get the selected items (1, 2).
get the lowest bid for thous items - MIN(b.amount)
display user names, who has given the bid - LEFT JOIN user p on p.id = b.user_id (this is not working or I'm doing something wrong)
[Note] I can't use sub-query, I'm doing this in doctrine2 (php code) which limits mysql sub-query
No, you are not necessarily fetching the user_id who has given the bid. You group by item_id, so you get one result row per item. So you are aggregating and for every column you say what value you want to see for that item. E.g.:
MIN(b.amount) - the minimum amount of the item's records
MAX(b.amount) - the maximum amount of the item's records
AVG(b.amount) - the avarage amount of the item's records
b.amount - one of the amounts of the item's records arbitrarily chosen (as there are many amounts and you don't specify which you want to see, the DBMS simply choses one of them)
This said, b.user_id isn't necessarily the user who made the lowest bid, but just one random user of the users who made a bid.
Instead find the minimum bids and join again with your bid table to access the realted records:
select bid.id, bid.item_id, bid.amount, user.id as user_id, user.name
from bid
join
(
select item_id, min(amount) as amount
from bid
group by item_id
) as min_bid on min_bid.item_id = bid.item_id and min_bid.amount = bid.amount
join user on user.id = bid.user_id
order by bid.amount, bid.item_id;
You can solve this using a subquery. I am not 100% sure if this is the most efficient way, but at least it works.
SELECT b1.id, b1.item_id, b1.amount, b1.user_id, p.name
FROM bid b1
LEFT JOIN user p ON p.id = b1.user_id
WHERE b1.id = (
SELECT b2.id
FROM bid b2
WHERE b2.item_id IN (1, 2)
ORDER BY b2.amount LIMIT 1
)
This first selects for the lowest bid with for item 1 or 2 and then uses the id of that bid to find the information you need.
Edit
You are saying that Doctrine does not support subqueries. I have not used Doctrine a lot, but something like this should work:
$subQueryBuilder = $entityManager->createQueryBuilder();
$subQuery = $subQueryBuilder
->select('b2.id')
->from('bid', 'b2')
->where('b2.item_id IN (:items)')
->orderBy('b2.amount')
->setMaxResults(1)
->getDql();
$queryBuilder = $entityManager->createQueryBuilder();
$query = $queryBuilder
->select('b1.id', 'b1.item_id', 'b1.amount', 'b1.user_id', 'p.name')
->from('bid', 'b1')
->leftJoin('user', 'p', 'with', 'p.id = b1.user_id')
->where('b1.id = (' . $subQuery . ')')
->setParameter('items', [1, 2])
->getQuery()->getSingleResult();

Mysql sort alphanumeric data

I have two tables tt1 and tt2 both contains same fields. I want to sort data by no from both table.
Table : tt1
CREATE TABLE IF NOT EXISTS `tt1` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`no` varchar(5) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
INSERT INTO `tt1` (`id`, `no`) VALUES
(1, '1A'),
(2, '3A'),
(3, '2A');
Table : tt2
CREATE TABLE IF NOT EXISTS `tt2` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`no` varchar(5) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
INSERT INTO `tt2` (`id`, `no`) VALUES
(1, '2A'),
(2, '3A'),
(3, '1A');
Expected output
ID | No
========
1 | 1A
3 | 1A
1 | 2A
3 | 2A
2 | 3A
2 | 3A
I want to ascending order of no field from both table as given output how I can get.
SQLFiddle
In this case using order by after every select is wrong (won't return desired output), because that will order both row-sets separately and union them after that.
What you want here is to order already combined data, therefore you should be using order by only once, after mysql makes union of tables (i.e. combines), because once it combines tables it has all data together but unordered, so when mysql sees order by it orders whole data for you.
Sample:
select * from `tt1`
union all
select * from `tt2`
order by `no`
Note: I've noticed you have wrong syntax in your fiddle. You need to add parentheses:
(select * from tt1 order by no)
union
(select * from tt2 order by no)
Note 2: Thanks #AlmaDo's notice. You should not use * with union queries. Because modification of your tables columns will break query. Use column names that you actually need. E.g. query in sample becomes:
select `id`, `no` from `tt1`
union all
select `id`, `no` from `tt2`
order by `no`