sql more than one keys in one field - mysql

I guess, the name of the question is strange, but I don't know how to state it correctly, so I'll try to describe the problem. Say, we have two tables:
1)
id | unit_name | strength | health
1 | private | 10 | 10
2 | general | 5 | 5
n | someone | 123 | 321
2)
id | rules | who_has_this_rule
1 | rule1 | 1
2 | rule2 | 2
3 | rule3 | 1
4 | rule4 | 1
The field "who_has_this_rule" correspondes with id of the first table.
But I can't figure out what to do if a unit should have more than one rule in his profile. For example, private should have rule1 and rule4. How to do this?

What you need is a join table.
A third table to join rule and units
unit_rules
id | unit_id | rule_id
1 | 1 | 1
2 | 1 | 4
3 | 2 | 1
You can have a primary key(id) or compound key(unit_id and rule_id)

Related

SQL - Aggregate all EXCEPT group

Consider the following sample table from a soccer tournament (let's call this table matches)
+----------+---------+--------------+
| match_id | club_id | goals_scored |
+----------+---------+--------------+
| 1 | 1 | 1 |
| 1 | 2 | 0 |
| 2 | 1 | 1 |
| 2 | 3 | 1 |
| 3 | 1 | 0 |
| 3 | 4 | 2 |
| 4 | 2 | 2 |
| 4 | 3 | 4 |
| 5 | 2 | 4 |
| 5 | 4 | 0 |
| 6 | 3 | 1 |
| 6 | 4 | 1 |
+----------+---------+--------------+
The resulting table we want should give us each club's total goals scored AND goals conceded:
+---------+--------------+----------------+
| club_id | goals_scored | goals_conceded |
+---------+--------------+----------------+
| 1 | 2 | 4 |
| 2 | 6 | 4 |
| 3 | 6 | 4 |
| 4 | 3 | 5 |
+---------+--------------+----------------+
Getting goals scored is straight forward enough...
SELECT SUM(goals_scored),
club_id
FROM matches
GROUP BY club_id
but I am absolutely flummoxed as to how to get it for each team's opponents.
I could, of course, construct a pretty complex array of subqueries to get there. If this were application-side work I'd likely just stuff it in a loop and iterate over each club to get there, but my use case requires a SQL answer if possible. Any thoughts?
edit: also if anyone has any better ideas on how to title this question, I'm all ears - I'm not really sure exactly how to describe this problem in the first place.
We can use a self-join approach here:
SELECT
m1.club_id,
SUM(m1.goals_scored) AS goals_scored,
SUM(m2.goals_scored) AS goals_conceded
FROM matches m1
INNER JOIN matches m2
ON m2.match_id = m1.match_id AND
m2.club_id <> m1.club_id
GROUP BY
m1.club_id
ORDER BY
m1.club_id;
This approach brings the goals conceded by each club to the other club, for each match, into a single row. We then just aggregate by club to get the two sums.

Mysql how to create EQUAL constraint?

Is there a way to create an "EQUAL" constraint in Mysql?
Imagine I have a table like this
tb_1:
id | id_fk | order | id_tb_2 | Column D
----------------------------------------
1 | 1 | 1 | 10 | AAA
2 | 1 | 2 | 20 | BBB
3 | 2 | 1 | 30 | CCC
4 | 3 | 1 | 40 | DDD
And:
tb_2:
id_tb_2 | id_type
------------------
10 | 100
20 | 200
30 | 300
40 | 400
And:
tb_3:
id_type | model | other_prop
--------------------------------
100 | model_A | A
200 | model_A | B
300 | model_B | C
400 | model_C | D
I want to make sure that tb_2.id_type.model are going to be equal for same values of id_fk and different order.
So I have a UNIQUE constraint for [id_fk, order]
And I need a EQUAL constraint for [id_fk, id_tb_2.id_type.model]
This way, I shouldn't be able to create a value in tb_1 like:
id | id_fk | order | id_tb_2 | Column D
----------------------------------------
1 | 1 | 1 | 10 | AAA
2 | 1 | 2 | 20 | BBB
5 | 1 | 3 | 30 | EEE <--- This has a different model in tb_2
How can I create one?
A solution using proper relational normalization is to drop the tb_1.Column D column.
Then the reference to tb_2.id_tb_2 and by extension to tb_3.id_type will be unambiguous.
tb_1:
id | id_fk | order | id_tb_2
----------------------------
1 | 1 | 1 | 10
2 | 1 | 2 | 20
3 | 2 | 1 | 30
4 | 3 | 1 | 40
In other words, by including Column D in tb_1, you're violating Third Normal Form, because Column D is an attribute that depends on something other than the primary key of tb_1.
But I'm not sure you've told us the full scope of the problem you're trying to solve.
You can't.
The only constraints that relate data from multiple tables are FOREIGN KEY constraints; and they are very limited about the relationship they check: a list of columns against another list of columns on another/same table.
All other relational constraints (such as UNIQUE or CHECK) have a table scope.

How to join 1 table to another table and get count result per date

I have 3 tables with corresponding fields.
Table 1 (List of Machines)
Machine_No | Machine_Description
1 | Hitachi
2 | Jet Printer
3 | Sumi
Table 2 (List of Manpower)
ID_Number | Employee_Name | Machine_No
1 | Taylor | 3
2 | James | 2
3 | David | 1
Table 3 (Actual Manpower use per machine)
Machine_No | Employee_Number | Date Posted
1 | 1 | 15-10-2019
1 | 2 | 15-10-2019
1 | 3 | 15-10-2019
Now... I want the results to go like this.
Machine_Now | Count(Employee Number) | Date_Posted
1 | 3 | 15-10-2019
Try this, it may work, if I understand your question correctly.
select Machine_No,Date_Posted,count(*) from Table3 group by Machine_No,Date_Posted;

Automatically inserting additional columns in MySQL 8.0 [duplicate]

This question already has an answer here:
MySQL pivot row into dynamic number of columns
(1 answer)
Closed 3 years ago.
Say I have a table like so
+----+----------+------+
| id | name | type |
+----+----------+------+
| 1 | apple | F |
| 1 | pear | F |
| 1 | cucumber | V |
| 2 | orange | F |
| 2 | grass | NULL |
| 2 | broccoli | V |
| 3 | flower | NULL |
| 3 | tomato | NULL |
+----+----------+------+
I want to end up with a table that counts the number of elements for each type (including NULL types) AND for each id, like this:
+----+-----------------+--------------+--------------+
| id | type_NULL_count | type_F_count | type_V_count |
+----+-----------------+--------------+--------------+
| 1 | 0 | 2 | 1 |
| 2 | 1 | 1 | 1 |
| 3 | 2 | 0 | 0 |
+----+-----------------+--------------+--------------+
This is rather easy to do, but is there a way (a query I can write or something else) such that when I go back and edit one of the type fields in the first table, I end up with a properly updated count table?
For example, let's say I want to add a new type (type X) and change the type field for flower from NULL to X. Is there a way to end up with the following table without having to rewrite the query or add more statements?
+----+-----------------+--------------+--------------+--------------+
| id | type_NULL_count | type_F_count | type_V_count | type_X_count |
+----+-----------------+--------------+--------------+--------------+
| 1 | 0 | 2 | 1 | 0 |
| 2 | 1 | 1 | 1 | 0 |
| 3 | 1 | 0 | 0 | 1 |
+----+-----------------+--------------+--------------+--------------+
I'm not sure if this is the best way to do this, so I am open to suggestions
Having a secondary table which it's number of columns changes based on your first table is not a viable option.
Do you need to keep the result in a table or it will be displayed as a report?
I think a better way to do this is using the SQL below calculate counts by id plus type and display using your data display tool the way you like it.
select id, type, count(*) count
from d
group by 1,2
order by 1,2
The output would be
id type count
1 F 2
1 V 1
2 F 1
2 V 1
2 1
3 X 1
3 1

Find unique/duplicated rows from has and belongs to many association

I have following DB structure:
Table cars:
+----+-----------------------+
| id | few other columns.... |
+----+-----------------------+
| 1 | ... |
| 2 | ... |
| 3 | ... |
+----+-----------------------+
Table properties:
+----+-------+
| id | name |
+----+-------+
| 1 | title |
| 2 | type |
| 3 | brand |
| 4 | color |
+----+-------+
Table cars_properties:
+----+--------+-------------+------------+
| id | car_id | property_id | txt |
+----+--------+-------------+------------+
| 1 | 1 | 1 | Volvo V70 |
| 2 | 1 | 2 | personal |
| 3 | 1 | 3 | Volvo |
| 4 | 1 | 4 | white |
| 5 | 2 | 1 | Volvo VV |
| 6 | 2 | 2 | personal |
| 7 | 2 | 3 | Volvo |
| 8 | 2 | 4 | blue |
| 9 | 3 | 1 | Volvo XXL |
| 10 | 3 | 2 | truck |
| 11 | 3 | 3 | Volvo |
| 12 | 3 | 4 | white |
+----+--------+-------------+------------+
I would like to get all cars that have unique/duplicated values in one or many properties. Currently I'm using this SQL pattern to get duplicates for car type and brand:
SELECT cars.id FROM cars
LEFT JOIN cars_properties AS cp_0 ON cp_0.car_id = cars.id AND cp_0.property_id = 2 # => type
LEFT JOIN cars_properties AS cp_1 ON cp_1.car_id = cars.id AND cp_1.property_id = 3 # => brand
INNER JOIN (
SELECT cp_0.txt AS type_txt, cp_1.txt AS brand_txt FROM cars
LEFT JOIN cars_properties AS cp_0 ON cp_0.car_id = cars.id AND cp_0.property_id = 2
LEFT JOIN cars_properties AS cp_1 ON cp_1.car_id = cars.id AND cp_1.property_id = 3
GROUP BY cp_0.txt, cp_1.txt
HAVING COUNT(cars.id) > 1
) dupes ON cp_0.txt=dupes.type_txt AND cp_1.txt=dupes.brand_txt;
And expected result is:
+----+
| id |
+----+
| 1 |
| 2 |
+----+
Explanation: Both cars with id = 1 and 2 has type and brand that is present in more than one car (multiple times).
As for unique cars, I'm just altering: HAVING COUNT(cars.id) = 1 and I want to find all rows where the combination of properties is present only in one car (once).
It works fine, but it's extremely slow with more than 2 properties I want to check.
I cannot change the DB structure, and I'm not sure how to optimize the query, or if there are better ways of achieving this.
It feels like I would need to implement counter table, where each property id and value (txt) would also store corresponding number of occurrences in cars, and update this counter on every insert/update/delete... But I still hope there is some better SQL, that could help. Do you know some? Any advice greatly appreciated, thanks!
PS: I tried to create fiddle for it, but after I build schema I cannot run any SQL on it. To quickly setup DB with data, you can check SQL Fiddle