SELECT rows with same id using different id as condition? - mysql

I have the following table:
+----+-------------+-----------------+-----------+----------------+
| id | link_id[FK] | category_id[FK] | parent_id[FK] | sort_order |
+----+-------------+-----------------+-----------+----------------+
| 1 2 1 1 1 |
| 2 2 133 1 2 |
| 3 3 2 2 1 |
| 4 3 200 2 2 |
| 5 3 333 200 3 |
| 6 4 1 1 1 |
| 7 5 3 3 1 |
| 8 5 133 3 2 |
| 9 5 223 133 3 |
| 10 5 456 223 4 |
+-----------------------------------------------------------------+
I need to be able to SELECT all rows with the same link_id, but using the category_id as the condition. So here is the example result I am looking for.
+----+-------------+-----------------+-----------+----------------+
| id | link_id[FK] | category_id[FK] | parent_id[FK] | sort_order |
+----+-------------+-----------------+-----------+----------------+
| 1 2 1 1 1 |
| 2 2 133 1 2 |
| 7 5 3 3 1 |
| 8 5 133 3 2 |
| 9 5 223 133 3 |
| 10 5 456 223 4 |
+-----------------------------------------------------------------+
I've tried this query, but it only returns the rows that equal category_id 133.
SELECT *
FROM table a
WHERE (a.object_id,a.category_id) IN (
SELECT b.link_id,b.link_id
FROM table b
WHERE b.category_id = 133
AND a.link_id = b.link_id
) ORDER BY a.sort_order
I've also tried a SELF JOIN, which is basically what I need, but then I get separate columns and I need them combined like above.

You first need to get the link_ids from the table where category_id = 133, then query the table for all the rows with that link_id.
SELECT * FROM `table`
WHERE link_id IN (
SELECT link_id FROM `table`
WHERE category_id = 133
)
ORDER BY link_id, sort_order
DEMO: http://sqlfiddle.com/#!9/67eb4/5
You should also be able to do this with a "self join", too:
SELECT a.* FROM `table` a
JOIN `table` b ON a.link_id = b.link_id
WHERE b.category_id = 133
ORDER BY a.link_id, a.sort_order
DEMO: http://sqlfiddle.com/#!9/67eb4/6

Related

How can I make recursive query in SQL?

I have tow tables like
users
id | name
_______________
1 | one
2 | two
3 | three
4 | four
5 | five
6 | six
employees
id | userId | reportedTo
_________________________
1 | 1 | null
2 | 2 | 1
3 | 3 | 2
4 | 4 | 3
5 | 5 | 4
6 | 6 | 5
I need something like
if I run query for some user then it will return all the record which reportedTo is this user and other users which reportedTo is reportedTo this user
Like:
if I run this query for user 1 then it will return all the record accept userId 1 from employees
id | userId | reportedTo
_________________________
2 | 2 | 1
3 | 3 | 2
4 | 4 | 3
5 | 5 | 4
6 | 6 | 5
if I run query for userId 4 then it will return recornds with userId 5 and 6 from employee table
id | userId | reportedTo
_________________________
5 | 5 | 4
6 | 6 | 5
can anyone help me with this.
Thanks in advance
You can use a recursive CTE:
with recursive cte(i, u, r) as (
select * from employees where reportedTo = 1
union all
select e.* from cte c join employees e on c.u = e.reportedTo
)
select c.i id, c.u userId, c.r reportedTo from cte c;
Output:
id
userId
reportedTo
2
2
1
3
3
2
4
4
3
5
5
4
6
6
5
See demo

MySql Query - Finding out the Intersect

I have 3 tables, user_tag, article_tag, article_ignored. I want to fetch only those articles of user_id = 48 for which at least one article tags matches with user tag. I am stuck in this since long time and don't have any idea how to achieve this.
The table structure is as follows:
article_ignored table
id | user_id
1 | 48
2 | 48
3 | 48
article_tag table
id | article_id | tag_id
1 | 1 | 1
2 | 1 | 5
3 | 1 | 7
4 | 2 | 2
5 | 2 | 8
6 | 3 | 3
7 | 3 | 2
user_tag table
id | user_id | tag_id
1 | 48 | 2
2 | 48 | 3
Required output:
article_ignored
id
2
3
You can use NOT EXISTS:
SELECT id, user_id
FROM article_ignored AS ai
WHERE EXISTS (SELECT 1
FROM article_tag AS at
JOIN user_tag AS ut ON at.tag_id = ut.tag_id
WHERE ai.id = at.article_id)
Demo here

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

Get product of different categories in mysql having separate limit for each id

I have a table named 'Product' like this
id | Category_id | ProductName | Quantity
...........................................
1 | 2 | ABC | 4
2 | 2 | DEF | 2
3 | 2 | GHI | 4
4 | 2 | JKL | 1
5 | 2 | MNO | 4
6 | 2 | PQR | 4
7 | 2 | STU | 4
8 | 3 | VWX | 4
9 | 3 | YZA | 4
10 | 3 | YAB | 4
11 | 3 | YCD | 4
12 | 4 | YEF | 4
13 | 5 | YGH | 4
I want to fetch product of Category_id 2 and 3 with limit 5.
Note : Product from each category must be display after applying limit.
Expected output: Fetched total five rows containing random data from each Category_id 2 and 3
Output :
id | Category_id | ProductName | Quantity
...........................................
5 | 2 | MNO | 4
6 | 2 | PQR | 4
7 | 2 | STU | 4
8 | 3 | VWX | 4
9 | 3 | YZA | 4
Data order may be changed
You can use variables:
SELECT id, Quantity, ProductName, Category_id
FROM (
SELECT id, Quantity, ProductName, Category_id,
#row_number:= CASE
WHEN #cid = Category_id THEN #row_number+1
ELSE 1
END AS row_number,
#cid:=Category_id
FROM (
SELECT id, Quantity, ProductName, Category_id
FROM products
WHERE Category_id IN (2, 3)
ORDER BY Category_id, RAND() ) s ) t
ORDER BY row_number, Category_id
LIMIT 5
The above query will select a total of 5 records from categories 2, 3, trying to balance the number of records fetched from each category: 3 records will be fetched from category 2, whereas 2 records will be fecthed from category 3.
SQL Fiddle Demo
One way to do is you may get the two queries separately and use UNION ALL
(
select *
from Product
where `Category_id ` = 2
order by ProductName desc
LIMIT 5
)
UNION ALL
(
select *
from Product
where `Category_id ` = 3
order by ProductName desc
LIMIT 5
)
So why not
7 | 2 | STU | 4
8 | 3 | VWX | 4
9 | 3 | YZA | 4
10 | 3 | YAB | 4
11 | 3 | YCD | 4
?
This result set corresponds perfectly with your stated criteria.

add record for every single id when join table

I need help with query
let say i have 2 tables
tableA
------------------------------------------
id | name
1 | ABC
2 | DEF
3 | GHI
tableB
------------------------------------------
id | anotherID | Amount
1 | 1 | 1000
2 | 1 | 2000
3 | 1 | 3000
1 | 2 | 4000
2 | 2 | 5000
1 | 3 | 6000
i need this list
jointable
------------------------------------------
id | anotherID | Amount
1 | 1 | 1000
2 | 1 | 2000
3 | 1 | 3000
1 | 2 | 4000
2 | 2 | 5000
3 | 2 | 0
1 | 3 | 6000
2 | 3 | 0
3 | 3 | 0
i need every single id have value even its 0 for every single anotherID value..
example :
tableA have 3 id & distinct anotherID tableB have 3 records so i will need 3 * 3 = 9 records..
how to do this with query?? i can do with left join and union but i want to know if there some more effective way to do it.
SQLFiddle demo
select T1.ID,T2.AnotherId,
COALESCE(T3.Amount,0) as Amount
FROM TableA as T1
CROSS JOIN (Select distinct anotherID FROM TableB) as T2
LEFT JOIN TableB as T3 on (T1.id=T3.id)
AND
(T2.anotherID=T3.anotherID)
Order by T2.anotherId,T1.Id