MySQL insert category rows - mysql

I'm struggling describing the problem in the Title, let alone looking for a solution that may well exist, so please excuse, or point me to the thread. I need to pad rows, so I have 4 rows per Category as in the simplified scenario below:
mysql> select * from a;
+-------+
| Num |
+-------+
| 1 |
| 2 |
| 3 |
| 4 |
+-------+
mysql> select * from b;
+----------+------+------+
| Category | Num | Data |
+----------+------+------+
| X | 2 | 10 |
| X | 3 | 12 |
| X | 4 | 8 |
| Y | 1 | 0 |
| Y | 2 | 19 |
| y | 3 | 15 |
| y | 4 | 22 |
| Z | 2 | 10 |
+----------+------+------+
The result I need is always 4 rows per category (table b will always ever have one of Num = 1, 2, 3 or 4 per category), set data = null for the padded rows:
mysql> select * from c;
+----------+------+------+
| Category | Num | Data |
+----------+------+------+
| X | 1 | NULL |
| X | 2 | 10 |
| X | 3 | 12 |
| X | 4 | 8 |
| Y | 1 | 0 |
| Y | 2 | 19 |
| y | 3 | 15 |
| y | 4 | 22 |
| Z | 1 | NULL |
| Z | 2 | 10 |
| Z | 3 | NULL |
| Z | 4 | NULL |
+----------+------+------+
I created table a artificially, as I thought it may help with the (INSERT) query?!

You can cross join table a with all available categories in table b, and then bring table b with a left join.
select
c.category,
a.num,
b.data
from a
cross join (select distinct category from b) c
left join b on b.category = c.category and b.num = a.num
You can easily turn this to an insert query to another table:
insert into c(category, num, data)
select
c.category,
a.num,
b.data
from a
cross join (select distinct category from b) c
left join b on b.category = c.category and b.num = a.num

You need left join
select a.num, b.category , b.data
from tablea a
left join tableb b on a.num = b.num
and for insert
insert into tablec (num, category, data)
select a.num, b.category , b.data
from tablea a
left join tableb b on a.num = b.num
of if need an update for the exing rows
update tablec c
inner join (
select a.num, b.category , ifnull(b.data,0)
from tablea a
left join tableb b on a.num = b.num ) t t.num= c.num and t.category = c.category
set data = t.data

Related

MySQL JOIN two tables on closest matching value

I have the following situation:
tableA
+-------+-------+
| id | Value |
+-------+-------+
| 1 | 1000 |
| 2 | 20 |
| 3 | 62 |
| 4 | 0 |
+-------+-------+
tableB
+-------+--------+
| Value | Lookup |
+-------+--------+
| 10 | a |
| 20 | b |
| 30 | b |
| 40 | g |
| 50 | h |
| 60 | f |
| 70 | a |
| 80 | a |
| 90 | v |
| 100 | b |
+-------+--------+
And I need to return the lookup in table B that most closely matches the value field in table A. For example.
+-------+-------+--------+
| id | Value | Lookup |
+-------+-------+--------+
| 1 | 1000 | b |
| 2 | 20 | b |
| 3 | 62 | f |
| 4 | 0 | a |
+-------+-------+--------+
How can I go about doing this?
Here is an option using joins:
SELECT
a.Id, a.Value, b.Lookup
FROM tableA a
CROSS JOIN tableB b
INNER JOIN
(
SELECT a.Id, MIN(ABS(a.Value - b.Value)) AS min_abs_value
FROM tableA a
CROSS JOIN tableB b
GROUP BY a.Id
) t
ON a.Id = t.Id AND
ABS(a.Value - b.Value) = t.min_abs_value;
Demo
While this query does join to a subquery, the subquery is not correlated.
One way is to use a correlated subquery:
SELECT a.Id, a.Value,
(SELECT b.Lookup
FROM TableB AS b
ORDER BY ABS(a.Value - b.Value) LIMIT 1)
FROM TableA AS a
Demo here

Mysql how to combine GROUP BY and COUNT here

This must be quite easy, but I cannot find a good solution myself.
I have two tables:
file
+----+--------+
| id | system |
+----+--------+
| 1 | AA |
| 2 | AA |
| 3 | BB |
| 4 | AA |
+----+--------+
feature
+----+---------+------+
| id | file_id | name |
+----+---------+------+
| 1 | 1 | A |
| 1 | 2 | A |
| 1 | 2 | B |
| 1 | 3 | B |
| 1 | 3 | C |
| 1 | 4 | A |
| 1 | 4 | B |
| 1 | 4 | C |
+----+---------+------+
and I want to count how many times a feature was added to files with a specific system. For that, I have the following query:
SELECT f.name, COUNT(*) AS nr
FROM dossier d
JOIN feature f
ON f.file_id = d.id
WHERE d.system = 'AA'
AND d.id NOT IN (3157,3168,3192)
GROUP BY f.name
which gives the desired output:
+------+----+
| name | nr |
+------+----+
| A | 3 |
| B | 2 |
| C | 1 |
+------+----+
Now I also want to know the total amount of files with the same specific system. A simple separate query would be:
SELECT COUNT(*) FROM file WHERE system = 'AA' AND id NOT IN (3157,3168,3192)
I've added the extra AND id NOT IN (which is irrelevant for this example) just to show that the actual query is much more complex. If I use a separate query to get the total I would have to duplicate that complexity, so I want to avoid that by returning the total from the same query.
So how can I count the number of files in the first query?
Desired output:
+------+----+-------+
| name | nr | total |
+------+----+-------+
| A | 3 | 3 |
| B | 2 | 3 |
| C | 1 | 3 |
+------+----+-------+
Here is one way using Sub-query
SELECT f.NAME,
Count(*) AS nr,
(SELECT Count(*)
FROM FILE
WHERE system = 'AA'
AND id NOT IN ( 3157, 3168, 3192 )) as Total
FROM dossier d
JOIN feature f
ON f.file_id = d.id
WHERE d.system = 'AA'
AND d.id NOT IN ( 3157, 3168, 3192 )
GROUP BY f.NAME
Or Use CROSS JOIN
SELECT *
FROM (SELECT f.NAME,
Count(*) AS nr,
FROM dossier d
JOIN feature f
ON f.file_id = d.id
WHERE d.system = 'AA'
AND d.id NOT IN ( 3157, 3168, 3192 )
GROUP BY f.NAME) A
CROSS JOIN (SELECT Count(*) AS Total
FROM FILE
WHERE system = 'AA'
AND id NOT IN ( 3157, 3168, 3192 )) B

Work around for intersect keyword in mysql

I have requirement of getting intersection of some results in mysql DB. But after googling came to know that there is no mysql intersect keyword available . Following are my sample tables.
gene table
+------+--------+---------+
| id | symbol | test_id |
+------+--------+---------+
| -1 | A | -1 |
| 8 | A | 3 |
| 9 | G | 3 |
| -1 | A | -1 |
| -2 | B | -1 |
| -3 | C | -1 |
| 1 | A | 1 |
| 2 | B | 1 |
| 3 | C | 1 |
| 4 | B | 2 |
| 5 | C | 2 |
| 6 | D | 2 |
| 7 | E | 2 |
| 8 | A | 3 |
| 9 | G | 3 |
| 10 | F | 3 |
| 11 | C | 3 |
| 12 | C | 4 |
| 13 | G | 4 |
| 14 | F | 4 |
| 15 | M | 4 |
| 16 | N | 4 |
+------+--------+---------+
test table
+------+-------+
| id | name |
+------+-------+
| -1 | test0 |
| 3 | test3 |
| -1 | test0 |
| 1 | test1 |
| 2 | test2 |
| 3 | test3 |
| 4 | test4 |
+------+-------+
Now I want to formulate a query which will give me the tests which are common for provided genes. e.g. I will provide gene A, B, C and I should get the following result:
id name id symbol
---------------------------
-1 | test0 | -1 | A
-1 | test0 | -2 | B
-1 | test0 | -3 | C
1 | test1 | 1 | A
1 | test1 | 2 | B
1 | test1 | 3 | C
I just tried to form a query by following way but didn't work, getting empty resultset and if I use 'or' in where clause getting tests for all genes in where clause.
select distinct t.id, t.name, g.id, g.symbol from tests t
join genes g on t.id = g.test_id
where g.symbol = 'A' and g.symbol='B' and g.symbol='C';
Please help me to construct the query.
The trick is to filter the records with your criteria, then group by test.id to check that it matches all the criteria:
SELECT t.id
FROM tests AS t
INNER JOIN genes AS g
ON t.id = g.test_id
WHERE g.symbol in ('A','B','C')
GROUP BY t.id
HAVING COUNT(DISTINCT g.symbol) = 3;
So the key line is here:
HAVING COUNT(DISTINCT g.symbol) = 3;
If, like test 2, there is only a match on 'B', then the count will return 1 and the test will be excluded. The number of items you are checking for must match the number in the HAVING clause.
If you then need to get the full data out, you just need to join back to your table:
SELECT t.id, t.name, g.id, g.symbol
FROM genes AS g
INNER JOIN
( SELECT t.id, t.name
FROM tests AS t
INNER JOIN genes AS g
ON t.id = g.test_id
WHERE g.symbol in ('A','B','C')
GROUP BY t.id, t.name
HAVING COUNT(DISTINCT g.symbol) = 3
) t
ON t.id = g.test_id;
Example on SQL Fiddle
Change those AND conditions to OR condition like below cause at any point in time g.symbol can hold only one value and not multiple value. that's why you are getting empty result set.
select t.id, t.name, g.id, g.symbol from tests t
join genes g on t.id = g.test_id
where (g.symbol = 'A' or g.symbol='B' or g.symbol='C')
and g.test_id = 1;
(OR) use a IN operator like
select t.id, t.name, g.id, g.symbol from tests t
join genes g on t.id = g.test_id
where g.symbol in ('A','B','C')
and g.test_id = 1;

How to select distinct pairs in MySQL join (same table) with transitivity?

I'm facing a very poorly designed database with a non-normalized table X.
This table X should have a N:M relationship with another table Y.
The problem is that this relationship is currently 1:N and the jerry-rigged solution until now was to duplicate the entries when there was various registries to be related.
Simplifying, I have this:
| ID | TEXT | LOCATION_ID |
| 1 | foo | 1 |
| 2 | foo | 2 |
| 3 | bar | 1 |
| 4 | bar | 4 |
| 5 | bar | 3 |
I have to normalize this table. So, my first idea was try to obtain pairs of similar registries. Something like this:
| a.ID | b.ID |
| 1 | 2 |
| 3 | 4 |
| 3 | 5 |
Experimenting a little bit:
SELECT a.id, b.id
FROM mytable AS a
INNER JOIN mytable AS b
ON a.text = b.text AND a.id != b.id
GROUP BY a.id, b.id
This lead to a problem like this:
| a.ID | b.ID |
| 1 | 2 |
| 2 | 1 |
| 3 | 4 |
| 3 | 5 |
| 4 | 3 |
| 4 | 5 |
| 5 | 3 |
| 5 | 4 |
The pairs were duplicated.
After some digging, I realized that this was more efficient:
SELECT a.id, b.id
FROM mytable AS a
INNER JOIN mytable AS b
ON a.text = b.text AND a.id < b.id
GROUP BY a.id, b.id
So, I got this:
| a.ID | b.ID |
| 1 | 2 |
| 3 | 4 |
| 3 | 5 |
| 4 | 5 |
But I still need to get rid of that last register.
Group on only one side and take the MIN() of the other:
SELECT MIN(a.ID) a, b.ID b
FROM mytable a JOIN mytable b ON b.text = a.text AND b.ID > a.ID
GROUP BY b.ID
See it on sqlfiddle.

MySql Query Select sum and minimum record for 1 tables

I have 1 table , i want to query for specific column.
Aa -> is distinct,
Bb -> Sum B Total grouping by Aa,
Cc -> Minimum value C for specific Aa,
Bb1,Dd -> B,D value when C is minimum,
Ee -> distinct
Table 1
id | A | B | C | D | E |
------------------------
1 | 2 | 1 | 2 | 1 | A |
2 | 2 | 2 | 7 | 0 | A |
3 | 4 | 1 | 7 | 3 | B |
4 | 4 | 0 | 5 | 4 | B |
Result
Aa | Bb | Cc | Bb1 | Dd | Ee |
-----------------------------
2 | 3 | 2 | 1 | 1 | A |
4 | 1 | 5 | 0 | 4 | B |
my query give wrong result
SELECT
test.A as Aa,
SUM(test.B) as Bb,
min(test.C) as Cc,
test.D as Dd,
test.E as Ee
FROM `test`
group by A,E
SELECT
a.A as Aa,
SUM(a.B) as Bb,
min(a.C) as Cc,
b.b B1,
b.D DD,
a.E EE
FROM `Table1` a
INNER JOIN
(
SELECT a.*
FROM table1 a
INNER JOIN
(
SELECT A, MIN(C) min_C
FROM table1
GROUP BY A
) b ON a.a = b.a AND a.c = b.min_C
) b ON a.a = b.a
group by a.A, b.b
SQLFiddle Demo