Issue in SQL statement to get value in combination of 3 values - mysql

Prod_Oth_Id ||| Prod_Code ||||| Prod_Details |||||||||| Prod_Value |
1 | BR25MAON | 4 | 9 |
2 | BR25MAON | 5 | 10 |
3 | BR25MAON | 6 | 11 |
4 | BR25MABO | 4 | 9 |
5 | BR25MABO | 5 | 10 |
6 | BR25MABO | 6 | 17 |
7 | BR25GLON | 4 | 9 |
8 | BR25GLON | 5 | 16 |
9 | BR25GLON | 6 | 11 |
10 | BR25GLBO | 4 | 9 |
11 | BR25GLBO | 5 | 16 |
12 | BR25GLBO | 6 | 17 |
I have combination of 3 prod_value for eg 9,10,11
& I want to retrieve prodcode for for above combination which is BR25MAON
for eg if I got 9, 16, 17 then I must retrieve prodcode for this combination which is BR25GLBO

You can count the matches and non matches, if there are 3 Prod_Values to match, there should be 3 matches and 0 non matches;
# 9, 10, 11
SELECT DISTINCT a.Prod_Code FROM MyTable a GROUP BY Prod_Code
HAVING SUM(a.Prod_Value NOT IN (9,10,11)) = 0 # <-- No non matches
AND SUM(a.Prod_Value IN (9,10,11)) = 3; # <-- 3 matches
If you for example were matching (1,2), the query would change to;
# 1, 2
SELECT DISTINCT a.Prod_Code FROM MyTable a GROUP BY Prod_Code
HAVING SUM(a.Prod_Value NOT IN (1,2)) = 0 # <-- No non matches
AND SUM(a.Prod_Value IN (1,2)) = 2; # <-- 2 matches
SQLfiddle demo

If you want to get the product code for different product value then simply use this:
Select Distinct Prod_Code From TableName Where Prod_Value IN (9,10,11)
and it will return only BR25GLBO
If you need something else please explain your requirement in detail.

Related

Filtering Column based on values on Another table

I'am tying to get the specific columns whose name starts with some patterns.
table_1
abcA| abcB | abcC | xyD | mnE
1 | 2 | 3 | 4 | 5
6 | 7 | 8 | 9 | 10
11 | 12 | 13 | 14 | 15
From the above table i'am in need of the Output Like
abcA | abcB | abcC
1 | 2 | 3
6 | 7 | 8
11 | 12 | 13
The columns should be selected DYNAMICALLY by filtering like any column name starts with abc should me selected.
I Tried this Query
"select column_name from information_schema.columns
where table_name='table_1' and column_name like 'abc%';"
It gives a another table only with column names
column_name
abcA
abcB
abcC
But I want to get the values of that Column names.
Thanks
This is poor table design, and it is fairly difficult to write code which can select a dynamic column name. Here is the design I would suggest to you:
ID | name | pos
1 | abcA | 1
2 | abcB | 1
3 | abcC | 1
4 | xyD | 1
5 | mnE | 1
6 | abcA | 2
7 | abcB | 2
8 | abcC | 2
9 | xyD | 2
10 | mnE | 2
11 | abcA | 3
12 | abcB | 3
13 | abcC | 3
14 | xyD | 3
15 | mnE | 3
With this design in place, you only need a very simple query:
SELECT pos, GROUP_CONCAT(ID) AS ids
FROM yourTable
WHERE name LIKE 'abc%'
GROUP BY pos;

Get list of unique ids with consecutive numbers in a column

I'm not sure if it's possible to accomplish this with mysql query alone, but I have a table in the following format:
+----+-----------+-----------------+-------+
| id | recipe_id | ingredient_id | order |
+----+-----------+-----------------+-------|
| 1 | 1 | 26 | 2 |
+----+-----------+-----------------+-------|
| 2 | 1 | 95 | 1 |
+----+-----------+-----------------+-------|
| 3 | 1 | 42 | 3 |
+----+-----------+-----------------+-------|
| 4 | 2 | 22 | 1 |
+----+-----------+-----------------+-------|
| 5 | 2 | 15 | 2 |
+----+-----------+-----------------+-------|
| 6 | 2 | 1 | 3 |
+----+-----------+-----------------+-------|
| 7 | 3 | 95 | 1 |
+----+-----------+-----------------+-------|
| 8 | 3 | 33 | 2 |
+----+-----------+-----------------+-------|
| 9 | 3 | 23 | 3 |
+----+-----------+-----------------+-------|
I wanted to generate a list of all recipe_id that have a consecutive order numbers. ex: 1, 2, 3, and so on, (recipe_id 2 for example) but if the they're not, for example, recipe_id 1 has 2, 1, 3, so it's not in order.
I'm using php, but I wanted to know if this was possible with mysql alone first.
You can use this to get all recipes where the order numbers not increasing with the id
SELECT * FROM mytable AS a, mytable AS b
WHERE ((a.id < b.id AND a.order > b.order) OR (a.id > b.id AND a.order < b.order)) AND a.recipe_id = b.recipe_id
And this one to get all recipes with there orders as one row
SELECT recipe_id, MAX(`order`)-MIN(`order`)+1=COUNT(`order`), GROUP_CONCAT(`order`)
FROM mytable GROUP BY recipe_id
Its only a beginning but i hope it will help you
I create a fiddle https://www.db-fiddle.com/f/dakAHH5YXt3o1rahXFznLy/0

return a unique list from query result after removing duplicate rows from the table

I have two columns product_id, r_store_id which have a few rows with same values. Rest of the column rows have different values
I have duplicate rows with same r_store_id and product_id because every time I have to add new entries into this table. I want unique rows list with latest update_dt
(refer the DB table below).
id | m_store_id |r_store_id|product_id | amount |update_dt |
1 | 4 | 1 | 45 | 10 |18/03/5 |
2 | 4 | 1 | 45 | 100 |18/03/9 |
3 | 4 | 1 | 45 | 20 |18/03/4 |
4 | 5 | 2 | 49 | 10 |18/03/8 |
5 | 5 | 2 | 49 | 60 |18/03/2 |
6 | 9 | 3 | 45 | 19 |18/03/5 |
7 | 9 | 3 | 45 | 56 |18/03/3 |
My result should look like this:
id | m_store_id |r_store_id|product_id | amount |update_dt |
2 | 7 | 1 | 45 | 100 |18/03/9 |
4 | 5 | 2 | 49 | 10 |18/03/8 |
6 | 9 | 3 | 45 | 19 |18/03/5 |
I want to put this result in a list like this:
List<Sales> salesList = (List<Sales>) query.list();
I am not able to find an easy solution. Please help me with this!
We can select the chronologically most recent update for each store, and then join to get all the variables:
select a.*
from mytable a
join (select m_store_id, r_store_id, product_id, max(update_dt) as maxdate
from mytable
group by 1,2,3) b
on a.m_store_id=b.m_store_id
and a.r_store_id=b.r_store_id
and a.product_id=b.product_id
and a.update_dt = b.maxdate;

Mysql optimization and explode

I have the following query that displays the top 10 most drawn pairs of numbers from the whole
table
select
p, count(p) as frequency
from
(SELECT
id,
CASE power1 <= power2 WHEN TRUE THEN CONCAT(power1,"-",power2) ELSE CONCAT(power2,"-",power1)
END p
FROM power
UNION
SELECT
id,
CASE power1<=power3 WHEN TRUE THEN CONCAT(power1,"-",power3) ELSE CONCAT(power3,"-",power1) END p
FROM power
UNION
SELECT
id,
CASE power1<=power4 WHEN TRUE THEN CONCAT(power1,"-",power4) ELSE CONCAT(power4,"-",power1) END p
FROM power
UNION
...............................................
SELECT
id,
CASE power19<=power20 WHEN TRUE THEN CONCAT(power19,"-",power20) ELSE CONCAT(power20,"-",power19)
END p
FROM power) as b
group by
p
order by
frequency desc, p asc
limit
0, 10
How can I impose a limit to take just the first 100 lines in descending order by ID? The query would be like this:
ORDER BY id LIMIT 0,100
But I haven't been able to adapt it for the above.
Could the code be optimized more than that?
power1, Power2 are values from tables.... would it work if i would have a string like 3,4,5,6 and then explode "," and after that power1 becomes 3, power2 to become 4, etc?
I mean the table format to look something like this :
table2
LATER EDIT :
I have table like this :
Table: data
+----+----+-----+
| id | nr | set |
+----+----+-----+
| 1 | 52 | 1 |
| 2 | 47 | 1 |
| 3 | 4 | 1 |
| 4 | 3 | 1 |
| 5 | 77 | 1 |
| 6 | 71 | 1 |
| 7 | 6 | 1 |
| 8 | 41 | 1 |
| 9 | 15 | 1 |
| 10 | 79 | 1 |
| 11 | 35 | 2 |
| 12 | 50 | 2 |
| 13 | 16 | 2 |
| 14 | 1 | 2 |
| 15 | 32 | 2 |
| 16 | 77 | 2 |
| 17 | 30 | 2 |
| 18 | 7 | 2 |
| 19 | 20 | 2 |
| 20 | 28 | 2 |
| .. | .. | ... |
+----+----+-----+
I have like 34360 id
And the following query :
SELECT
`n1`.`nr` AS `num_1`,
`n2`.`nr` AS `num_2`,
COUNT(1) AS `total`
FROM (select * from data ORDER BY id DESC limit 0,1000) AS `n1`
JOIN `data` AS `n2`
ON `n1`.`set` = `n2`.`set` AND `n1`.`nr` < `n2`.`nr`
GROUP BY `n1`.`nr`, `n2`.`nr`
ORDER BY `total` DESC
LIMIT 20
And is working fine !
I would like to know how i can find out the pairs of numbers that have not been drawn together for the longest time. Per example:
1,42 (together, as a pair) has not been drawn for 24 draws
32,45-as a pair as well-has not been drawn for 22 draws
etc
Consider the following:
Un-normalised:
id power1 power2 power3 power4
1 4 9 10 16
2 6 12 15 19
3 2 4 6 7
4 3 8 15 17
5 2 10 11 14
6 4 10 12 19
7 1 4 9 11
Normalised:
id power value
1 1 4
1 2 9
1 3 10
1 4 16
2 1 6
2 2 12
2 3 15
2 4 19
3 1 2
3 2 4
3 3 6
3 4 7
4 1 3
4 2 8
4 3 15
4 4 17
5 1 2
5 2 10
5 3 11
5 4 14
6 1 4
6 2 10
6 3 12
6 4 19
7 1 1
7 2 4
7 3 9
7 4 11
So...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL
,power INT NOT NULL
,value INT NOT NULL
,PRIMARY KEY(id,power)
);
INSERT INTO my_table VALUES
(1,1,4),(1,2,9),(1,3,10),(1,4,16),
(2,1,6),(2,2,12),(2,3,15),(2,4,19),
(3,1,2),(3,2,4),(3,3,6),(3,4,7),
(4,1,3),(4,2,8),(4,3,15),(4,4,17),
(5,1,2),(5,2,10),(5,3,11),(5,4,14),
(6,1,4),(6,2,10),(6,3,12),(6,4,19),
(7,1,1),(7,2,4),(7,3,9),(7,4,11);
SELECT LEAST(x.value,y.value)a -- LEAST/GREATEST is only necessary in the event that
, GREATEST(x.value,y.value) b -- power1 value may be greater than powerN value
, COUNT(*) freq
FROM my_table x
JOIN my_table y
ON y.id = x.id
AND y.power < x.power
GROUP
BY LEAST(x.value, y.value) -- again only necessary if using LEAST/GREATEST above
, GREATEST(x.value,y.value)
ORDER
BY freq DESC
, a
, b;
+----+----+------+
| a | b | freq |
+----+----+------+
| 4 | 9 | 2 |
| 4 | 10 | 2 |
| 12 | 19 | 2 |
| 1 | 4 | 1 |
| 1 | 9 | 1 |
| 1 | 11 | 1 |
| 2 | 4 | 1 |
| 2 | 6 | 1 |
| 2 | 7 | 1 |
| 2 | 10 | 1 |
| 2 | 11 | 1 |
| 2 | 14 | 1 |
| 3 | 8 | 1 |
| 3 | 15 | 1 |
| 3 | 17 | 1 |
| 4 | 6 | 1 |
| 4 | 7 | 1 |
| 4 | 11 | 1 |
| 4 | 12 | 1 |
| 4 | 16 | 1 |
| 4 | 19 | 1 |
| 6 | 7 | 1 |
| 6 | 12 | 1 |
| 6 | 15 | 1 |
| 6 | 19 | 1 |
| 8 | 15 | 1 |
| 8 | 17 | 1 |
| 9 | 10 | 1 |
| 9 | 11 | 1 |
| 9 | 16 | 1 |
| 10 | 11 | 1 |
| 10 | 12 | 1 |
| 10 | 14 | 1 |
| 10 | 16 | 1 |
| 10 | 19 | 1 |
| 11 | 14 | 1 |
| 12 | 15 | 1 |
| 15 | 17 | 1 |
| 15 | 19 | 1 |
+----+----+------+
While I fully agree with #Strawberry about normalising your data, the following is an example of how to possibly do it with your current data structure (not tested).
SELECT CASE a.power_val <= b.power_val WHEN TRUE THEN CONCAT(a.power_val,"-",b.power_val) ELSE CONCAT(b.power_val,"-",a.power_val) END p,
COUNT(a.id) as frequency
FROM
(
SELECT id,1 AS power_col, power1 AS power_val FROM power UNION
SELECT id,2, power2 FROM power UNION
SELECT id,3, power3 FROM power UNION
SELECT id,4, power4 FROM power UNION
SELECT id,5, power5 FROM power UNION
SELECT id,6, power6 FROM power UNION
SELECT id,7, power7 FROM power UNION
SELECT id,8, power8 FROM power UNION
SELECT id,9, power9 FROM power UNION
SELECT id,10, power10 FROM power UNION
SELECT id,11, power11 FROM power UNION
SELECT id,12, power12 FROM power UNION
SELECT id,13, power13 FROM power UNION
SELECT id,14, power14 FROM power UNION
SELECT id,15, power15 FROM power UNION
SELECT id,16, power16 FROM power UNION
SELECT id,17, power17 FROM power UNION
SELECT id,18, power18 FROM power UNION
SELECT id,19, power19 FROM power UNION
SELECT id,20, power20 FROM power
ORDER BY id DESC
LIMIT 2000
) a
INNER JOIN
(
SELECT id, 1 AS power_col, power1 AS power_val FROM power UNION
SELECT id, 2, power2 FROM power UNION
SELECT id,3, power3 FROM power UNION
SELECT id,4, power4 FROM power UNION
SELECT id,5, power5 FROM power UNION
SELECT id,6, power6 FROM power UNION
SELECT id,7, power7 FROM power UNION
SELECT id,8, power8 FROM power UNION
SELECT id,9, power9 FROM power UNION
SELECT id,10, power10 FROM power UNION
SELECT id,11, power11 FROM power UNION
SELECT id,12, power12 FROM power UNION
SELECT id,13, power13 FROM power UNION
SELECT id,14, power14 FROM power UNION
SELECT id,15, power15 FROM power UNION
SELECT id,16, power16 FROM power UNION
SELECT id,17, power17 FROM power UNION
SELECT id,18, power18 FROM power UNION
SELECT id,19, power19 FROM power UNION
SELECT id,20, power20 FROM power
ORDER BY id DESC
LIMIT 2000
) b
ON a.id = b.id
AND a.power_col != b.power_col
GROUP BY p
ORDER BY frequency DESC, p ASC
LIMIT 0,10
Note using normalised data structures would likely be far quicker.
EDIT
Think something like the following might give you what you need.
The big sub query is to get every possible combination (idea is to also cope with pairs that have never been used), with the first number being smaller than the 2nd just for consistency. This is then joined against the tables of data to get the matching numbers and the respective id fields. Then uses MIN to get the smallest id:-
SELECT all_combo.num_1,
all_combo.num_2,
MIN(d1.id)
FROM
(
SELECT sub0.nr AS num_1,
sub1.nr AS num_2
FROM
(
SELECT DISTINCT nr
FROM data
) sub0
INNER JOIN
(
SELECT DISTINCT nr
FROM data
) sub1
WHERE sub0.nr < sub1.nr
) all_combo
LEFT OUTER JOIN data d1 ON all_combo.num_1
LEFT OUTER JOIN data d2 ON all_combo.num_2 AND d1.set = d2.set
GROUP BY all_combo.num_1,
all_combo.num_2

How to get this specific user rankings query in mysql?

I've got tbl_items in my user database that I want to sort user rankings on a particular item with certain id (514). I have test data on my dev environment with this set of data:
mysql> select * from tbl_items where classid=514;
+---------+---------+----------+
| ownerId | classId | quantity |
+---------+---------+----------+
| 1 | 514 | 3 |
| 2 | 514 | 5 |
| 3 | 514 | 11 |
| 4 | 514 | 46 |
| 5 | 514 | 57 |
| 6 | 514 | 6 |
| 7 | 514 | 3 |
| 8 | 514 | 27 |
| 10 | 514 | 2 |
| 11 | 514 | 73 |
| 12 | 514 | 18 |
| 13 | 514 | 31 |
+---------+---------+----------+
12 rows in set (0.00 sec)
so far so good :) I wrote the following query:
set #row=0;
select a.*, #row:=#row+1 as rank
from (select a.ownerid,a.quantity from tbl_items a
where a.classid=514) a order by quantity desc;
+---------+----------+------+
| ownerid | quantity | rank |
+---------+----------+------+
| 11 | 73 | 1 |
| 5 | 57 | 2 |
| 4 | 46 | 3 |
| 13 | 31 | 4 |
| 8 | 27 | 5 |
| 12 | 18 | 6 |
| 3 | 11 | 7 |
| 6 | 6 | 8 |
| 2 | 5 | 9 |
| 7 | 3 | 10 |
| 1 | 3 | 11 |
| 10 | 2 | 12 |
+---------+----------+------+
12 rows in set (0.00 sec)
that ranks correctly the users. However in a table with lots of records, I need to do the following:
1) be able to get small portion of the list, around where the user ranking actually resides, something that would get me the surrounding records, preserving the overall rank:
I tried to do these things with setting a user variable to the ranking of the current user and by using offset and limit, but couldn't preserve the overall ranking.
This should get me something like the following (for instance ownerId=2 and surroundings limit 5:
+---------+----------+------+
| ownerid | quantity | rank |
+---------+----------+------+
| 3 | 11 | 7 |
| 6 | 6 | 8 |
| 2 | 5 | 9 | --> ownerId=2
| 7 | 3 | 10 |
| 1 | 3 | 11 |
+---------+----------+------+
5 rows in set (0.00 sec)
2) I'd also need another query (preferably single query) that gets me the top 3 places + the ranking of particular user with certain id, preferably with a single query, no matter if he's among the top 3 places or not. I couldn't get this as well
It would look like the following (for instance ownerId=2 again):
+---------+----------+------+
| ownerid | quantity | rank |
+---------+----------+------+
| 11 | 73 | 1 |
| 5 | 57 | 2 |
| 4 | 46 | 3 |
| 2 | 5 | 9 | --> ownerId=2
+---------+----------+------+
4 rows in set (0.00 sec)
Also I'm in a bit of a concern about the performance of the queries on a table with millions of records...
Hope someone helps :)
1) 5 entries around a given id.
set #row=0;
set #rk2=-1;
set #id=2;
select b.* from (
select a.*, #row:=#row+1 as rank, if(a.ownerid=#id, #rk2:=#row, -1) as rank2
from (
select a.ownerid,a.quantity
from tbl_items a
where a.classid=514) a
order by quantity desc) b
where b.rank > #rk2 - 3
limit 5;
Though you'll get an extra column rank2: you probably want to filter it out by explicit list of columns instead of b.*. Maybe it's possible whith a having clause rather than an extra nesting.
2) 3 top ranked entries + 1 specific id
select b.* from (
select a.*, #row:=#row+1 as rank
from (
select a.ownerid,a.quantity
from tbl_items a
where a.classid=514) a
order by quantity desc) b
where b.rank < 4 or b.ownerid=#id