MySQL related items query - mysql

I have related products table like this:
product_id | related_product_id
1 | 2
1 | 3
1 | 4
2 | 1
3 | 1
4 | 1
But instead I would like to insert new related product ids so they all match. I.E. If product 1 has 2,3,4 I wan't that products 2,3,4 also have the same related ids which are missing.
Not sure how it's called but is this possible? Many thanks.

You can use a SELECT query as the source of data in an INSERT
INSERT INTO related_products (product_id, related_product_id)
SELECT r1.product_id, r2.related_product_id
FROM related_products AS r1
CROSS JOIN related_products AS r2
WHERE r1.product_id != 1
AND r2.product_id = 1
This join will get all of product 1's related products and combine them with all the other product IDs.

You can give this query a try (untested, make a backup first!):
insert into related_products (product_id,related_product_id) (select related_product_id, product_id from related products);

I would suggest to user bidirectional condition to get the inter related products. For example if you apply condition on single column product_id, you will not get visa-versa result. But, if you check that condition on both column, you will get the result.
For example:
select related_product_id, product_id from products where related_product_id=1 OR product_id=1
This will give your related product id in either related_product_id or product_id.
Same you can get it for product 2 i.e.
select related_product_id, product_id from products where related_product_id=2 OR product_id=2
This will give all your related product id in either related_product_id or product_id.

Related

With SQL how to select rows that the joined table matches 2 values

If we have a table, orders:
Order ID
Order Table
1
100
1
50
And we have a table, OrderProducts:
OrderID
Product ID
ProductName
ProductType
1
1
ProductOne
Small
1
2
ProductTwo
Big
1
3
ProductThree
Small
2
4
ProductFour
Big
2
5
ProductFive
Big
How do I use SQL to return only the Orders that contain products of 2 specific types. In the scenario above I want to return only products with ProductType = 'Small' and 'Big' but ignore orders like "2" which only have products of two 'Big' on.
Results:
Order ID
1
I have tried creating a view to help, inner joins with multiple clauses but I am struggling. I am using MySQL for this and think my logic is simply "out".
Any advice is appreciated.
Aggregation is one method:
select order_id
from orderproducts op
where ProductType in ('Small', 'Big')
group by order_id
having count(distinct ProductType) = 2;

Query that selects IDs of pair of subsidiaries that have exactly the same movies

So, I have an intermediate table in MySQL called "Inventory", with two PKs: idMovie and idSubsidiary. That table looks like this:
----------------------------------
idMovie (int) | idSubsidiary (int)
----------------------------------
0 | 0
2 | 0
1 | 1
3 | 2
----------------------------------
I want to select the IDs of the pair of subsidiaries that have exactly the same movies.
For that, I was thinking about something like this:
select distinct inv1.idSubsidiary, inv2.idSubsidiary
from inventory inv1
join inventory inv2
on inv1.idSubsidiary <> inv2.idSubsidiary
where not exists (
SELECT i1.idSubsidiary, i1.idMovie , i2.idSubsidiary, i2.idMovie
FROM inventory i1
INNER JOIN inventory i2 ON i1.idMovie = i2.idMovie
WHERE (i1.idSubsidiary= inv1.idSubsidiary and i2.idSubsidiary= inv2.idSubsidiary
AND i2.idSubsidiary IS NULL
)
The result I'm looking for would be something like this:
idSubsidiary | idSubsidiary
---------------------------
0 | 1
3 | 4
So, subsidiary 0 and 1 have the same identical movies on the inventory, same with 3 and 4.
However, the previously shown query is not working. Basically, the query looks up for couples of SubsidiaryID's on Inventory and then runs a nested query to find if the first Subsidiary have any movie that the second Subsidiary does not have. If they don't, it selects both.
However, the nested query is not working. As I said, I want to do a left join of the same table without the inner part.
Any help is much appreciated :)
The simplest method in MySQL is to do double aggregation:
select movies, group_concat(idSubsidiary) as subsidiaries
from (select i.idSubsidiary, group_concat(idMovie order by idMovie) as movies
from inventory i
group by i.idSubsidiary
) s
group by movies
having count(*) > 1;
Each row in the result set is a set of movies with the set of ids that have exactly those movies. Those are the duplicates.

MySQL: How to have sum of each column for many columns

I have a table like this:
client_id | Product1 | Product2 | ... | Product170
--------------------------------------------------
4 | Null | 4 | ... | 5
32 | 5 | 3 | ... | Null
22 | 4 | 1 | ... | 3
I want to have the totals for each of my Products. I want a view, or something similar, like this:
product_id | Total
--------------------------------------------------
Product1 | 9
Product2 | 8
...
Preferably leaving out Products that have a sum of 0.
Am I able to do this? I have many columns so I would rather not have a SELECT statement calling each individual column by name.
(Context: This table holds orders for a business. A client will order some products and it is stored here. If you have a better way to organize this info, please let me know).
Am I able to do this?
Yes, by writing an extremely long query with UNION.
Example:
SELECT 'Product1' as product_id, SUM(Product1) as Total
FROM table
UNION
SELECT 'Product2' as product_id, SUM(Product2) as Total
FROM table
UNION
...
Obviously this is not practical, so...
If you have a better way to organize this info, please let me know
A better way to organize this info would be to normalize it using a products table (with a unique id) and a junction table (e.g. client_products). This table contains 3 columns : client_id, product_id and n (the number of product, or whatever your number represents). The primary key is (client_id, product_id), and add an index to product_id.
You can very easily query this model with SELECT product_id, SUM(n) FROM client_products GROUP BY product_id.
You can write the query like
SELECT sum(Product1) as Product1, sum(Product2) as Product1 FROM `product`
It will give you the total of each product but in one row and having product name as column. You can also add the > 0 condition in where clause

SQL, Find orders where strict critera is met, Match item, not if other items purchased

I have stumped all the IT people at my work with this one, so wondeirng if anyone can help.
I need to extract from an order table anyone who has only purchased a specific product type, (if they have order the product type and any other product types i dont want to know who you are)
for example the table is roughly
---------------------------------------------------------------------------------------
Order ID | item code | Name |
----------------------------------------------------------------------------------------
1 | ADA | item 1
2 | ADA | item 1
2 | GGG | item 2
3 | ADA | item 1
----------------------------------------------------------------------------------------
So i want to find all the order IDs of people who only purchased item code ADA, BUT not if they purchased over items, so the output of this query should be order ID 1 & 3 and skipping order 2 as this had a different item.
Would really appriciate it if anyone could help.
Assuming an order can't have multiple records with the same ItemCode, you could use:
SELECT *
FROM Orders
WHERE OrderID IN (
SELECT OrderID
FROM Orders
GROUP BY OrderID HAVING COUNT(*) = 1
)
AND ItemCode = 'ADA'
If an order could have multiple records with the same ItemCode then you'd have to change the SELECT * to SELECT DISTINCT * and then COUNT(*) to COUNT(DISTINCT ItemCode)
Based on your current explanation and example, the below should work. However, there are outstanding questions in the comments which may change the actual correct solution.
SELECT
O.OrderId, MAX(itemCode), MAX(Name)
FROM
Orders O
INNER JOIN
(SELECT
OrderId
FROM
Orders
WHERE
itemCode = 'ADA') ADA
ON
O.OrderId = ADA.OrderId
GROUP BY
O.OrderId
HAVING
COUNT(*) = 1

How to filter duplicates within row using Distinct/group by with JOINS

For simplicity, I will give a quick example of what i am trying to achieve:
Table 1 - Members
ID | Name
--------------------
1 | John
2 | Mike
3 | Sam
Table 1 - Member_Selections
ID | planID
--------------------
1 | 1
1 | 2
1 | 1
2 | 2
2 | 3
3 | 2
3 | 1
Table 3 - Selection_Details
planID | Cost
--------------------
1 | 5
2 | 10
3 | 12
When i run my query, I want to return the sum of the all member selections grouped by member. The issue I face however (e.g. table 2 data) is that some members may have duplicate information within the system by mistake. While we do our best to filter this data up front, sometimes it slips through the cracks so when I make the necessary calls to the system to pull information, I also want to filter this data.
the results SHOULD show:
Results Table
ID | Name | Total_Cost
-----------------------------
1 | John | 15
2 | Mike | 22
3 | Sam | 15
but instead have John as $20 because he has plan ID #1 inserted twice by mistake.
My query is currently:
SELECT
sq.ID, sq.name, SUM(sq.premium) AS total_cost
FROM
(
SELECT
m.id, m.name, g.premium
FROM members m
INNER JOIN member_selections s USING(ID)
INNER JOIN selection_details g USING(planid)
) sq group by sq.agent
Adding DISTINCT s.planID filters the results incorrectly as it will only show a single PlanID 1 sold (even though members 1 and 3 bought it).
Any help is appreciated.
EDIT
There is also another table I forgot to mention which is the agent table (the agent who sold the plans to members).
the final group by statement groups ALL items sold by the agent ID (which turns the final results into a single row).
Perhaps the simplest solution is to put a unique composite key on the member_selections table:
alter table member_selections add unique key ms_key (ID, planID);
which would prevent any records from being added where the unique combo of ID/planID already exist elsewhere in the table. That'd allow only a single (1,1)
comment followup:
just saw your comment about the 'alter ignore...'. That's work fine, but you'd still be left with the bad duplicates in the table. I'd suggest doing the unique key, then manually cleaning up the table. The query I put in the comments should find all the duplicates for you, which you can then weed out by hand. once the table's clean, there'll be no need for the duplicate-handling version of the query.
Use UNIQUE keys to prevent accidental duplicate entries. This will eliminate the problem at the source, instead of when it starts to show symptoms. It also makes later queries easier, because you can count on having a consistent database.
What about:
SELECT
sq.ID, sq.name, SUM(sq.premium) AS total_cost
FROM
(
SELECT
m.id, m.name, g.premium
FROM members m
INNER JOIN
(select distinct ID, PlanID from member_selections) s
USING(ID)
INNER JOIN selection_details g USING(planid)
) sq group by sq.agent
By the way, is there a reason you don't have a primary key on member_selections that will prevent these duplicates from happening in the first place?
You can add a group by clause into the inner query, which groups by all three columns, basically returning only unique rows. (I also changed 'premium' to 'cost' to match your example tables, and dropped the agent part)
SELECT
sq.ID,
sq.name,
SUM(sq.Cost) AS total_cost
FROM
(
SELECT
m.id,
m.name,
g.Cost
FROM
members m
INNER JOIN member_selections s USING(ID)
INNER JOIN selection_details g USING(planid)
GROUP BY
m.ID,
m.NAME,
g.Cost
) sq
group by
sq.ID,
sq.NAME