Using foreign key - sql

I am new to foreign key, but I understand the concept very well.
I have found lot of documentation on how to create / delete them but not how to use them. My schema is as follows.
Stock table:
PartID | Model | Type | Vendor
------------------------------
1 | DDr2 | RAM | shop1
2 | DDr3 | RAM | shop1
3 | WD1 | HDD | shop2
4 | WD2 | HDD | shop2
Then product Table
ProdID | Name | PartID1 | PartID2 ...
1 | PC1 | 1 | 2
2 | PC1 | 3 | 4
How do I use select to get
| PC1 | DDr2 | DDR3 |
| PC1 | WD1 | WD2 |
with PartID2 and PartID3 foreign key linked to PartID primary key?

The concept of Foreign Keys is to link the IDs in one table to the lisk of unique IDs in another. In your example, you have unique parts with unique IDs and Products that can use those parts, so in your product table, you could have multiple part IDs being used in multiple rows.
Foreign Keys are used to keep referential integrity in your database, you can use joins to get the Data you want:
SELECT A.NAME,
B.Model,
C.Model
FROM PRODUCTS A
INNER JOIN PARTS B ON B.PARTID1 = A.PARTID
INNER JOIN PARTS C ON C.PARTID1 = A.PARTID
WHERE A.PRODID = 1

The short answer is you could do
select p.name, a.model as part1, b.model as part2, c.model as part3
from product p, stock a, stock b, stock c
where p.partid1 = a.partid and p.partid2 = b.partid and p.partid3 = c.partid
The longer answer is that this isn't really a good table design for what you're trying to do. It assumes that you always have a fixed number of parts for any item (or at least no more than some fixed number). A better design would be:
Part Table:
partID | model | type | vendor
Product Table:
productID | name
Product_Parts Table:
productID | partID
where productID in Product_parts is a foreign key into Product and partID is a foreign key into the Part table.

SELECT s1.Name, p1.Model, p2.Model FROM stock st
INNER JOIN product p1
ON st.PartID1 = p1.PartID1
INNER JOIN product p2
ON st.PartID2 = p2.PartID1
Take one JOIN at the time first join stock and parts table
then again join result of this join to parts table.
SQL parser will use parts table as two separate tables an so you can have two results from same tabe in single row.

you can join in a table more than once in the same sql statement. in this case, you need to join your stock table twice, once to get the name of each part in your product.
SELECT pr.ProdID, s1.Model, s2.Model
FROM Product pr, Stock s1, Stock s2
WHERE pr.PartID1 = s1.PartID
AND pr.PartID2 = s2.PartID

Using a LEFT OUTER JOIN means that the product will still be returned event if the Part1ID or Part2ID values are set to NULL.
SELECT P.Name,
S1.Model,
S2.Model
FROM Product P
LEFT OUTER JOIN Stock S1 ON P.PartID1 = S1.PartID
INNER JOIN Stock S2 ON P.PartID2 = S2.PartID

Related

SQL Server avoid repeat same joins

I´m doing the query below where I´m repeating the same joins multiple times, there is a better way to do it? (SQL Server Azure)
Ex.
Table: [Customer]
[Id_Customer] | [CustomerName]
1 | Tomy
...
Table: [Store]
[Id_Store] | [StoreName]
1 | SuperMarket
2 | BestPrice
...
Table: [SalesFrutes]
[Id_SalesFrutes] | [FruteName] | [Fk_Id_Customer] | [Fk_Id_Store]
1 | Orange | 1 | 1
...
Table: [SalesVegetable]
[Id_SalesVegetable] | [VegetableName] | [Fk_Id_Customer] | [Fk_Id_Store]
1 | Pea | 1 | 2
...
Select * From [Customer] as C
left join [SalesFrutes] as SF on SF.[Fk_Id_Customer] = C.[Id_Customer]
left join [SalesVegetable] as SV on SV.[Fk_Id_Customer] = C.[Id_Customer]
left join [Store] as S1 on S1.[Id_Store] = SF.[Fk_Id_Store]
left join [Store] as S2 on S1.[Id_Store] = SV.[Fk_Id_Store]
In my real case, I have many [Sales...] to Join with [Customer] and many other tables similar to [Store] to join to each [Sales...]. So it starts to scale a lot the number on joins repeating. There is a better way to do it?
Bonus question: I do like also to have FruteName, VegetableName, StoreName, and each Food table name under the same column.
The Expected Result is:
[CustomerName] | [FoodName] | [SalesTableName] | [StoreName]
Tomy | Orange | SalesFrute | SuperMarket
Tomy | Pea | SalesVegetable | BestPrice
...
Thank you!!
So based on the information provided, I would have suggested the below, to use a cte to "fix" the data model and make writing your query easier.
Since you say your real-world scenario is different to the info provided it might not work for you, but could still be applicable if you have say 80% shared columns, you can just use placeholder/null values where relevant for unioning the data sets and still minimise the number of joins eg to your store table.
with allSales as (
select Id_SalesFrutes as Id, FruitName as FoodName, 'Fruit' as SaleType, Fk_Id_customer as Id_customer, Fk_Id_Store as Id_Store
from SalesFruits
union all
select Id_SalesVegetable, VegetableName, 'Vegetable', Fk_Id_customer, Fk_Id_Store
from SalesVegetable
union all... etc
)
select c.CustomerName, s.FoodName, s.SaleType, st.StoreName
from Customer c
join allSales s on s.Id_customer=c.Id_customer
join Store st on st.Id_Store=s.Id_Store

SQL query to match similar customers

I'm trying to find the query in order to match similar customers.
To simplify the situation consider this scenario:
I have a table which contains a customer name and product purchased.
customer name can have multiple purchases of same and different products.
So firstly I can take distinct customer name and product name, so I see all customers and all products they purchased at least once.
Now I want a query to show me a sort of matching customers, according to the product they both purchased, so I want to count the similar products they purchased.
So I want to see for each pair of customers (pairing all the table) the amount of similar product they purchased.
Lets say the raw data is:
CustomerName | ProductName
A | 1
A | 2
A | 1
A | 3
B | 1
B | 2
B | 4
C | 2
Then I want to see the result of:
CustomerName1 | CustomerName2 | CountSimilarity
A | B | 2
A | C | 1
B | C | 1
And so on for all pairs of customers that have at least 1 similar product purchasing
Any suggestions how to approach this query?
The environment is SQL Server.
Thanks
Here is a self join approach:
SELECT t1.CustomerName, t2.CustomerName, COUNT(*) AS CountSimilarity
FROM yourTable t1
INNER JOIN yourTable t2
ON t1.ProductName = t2.ProductName
WHERE
t1.CustomerName < t2.CustomerName
GROUP BY
t1.CustomerName, t2.CustomerName;
Two records are joined together above if their products match. Note that the inequality in the WHERE clause ensures that customer pairs do not appear in duplicate.

INNER JOIN Need to use column value twice in results

I've put in the requisite 2+ hours of digging and not getting an answer.
I'd like to merge 3 SQL tables, where Table A and B share a column in common, and Table B and C share a column in common--Tables A and C do not.
For example:
Table A - entity_list
entity_id | entity_name | Other, irrelevant columns
Example:
1 | Microsoft |
2 | Google |
Table B - transaction_history
transaction_id | purchasing_entity | supplying_entity | other, irrelevant columns
Example:
1 | 2 | 1
Table C - transaction_details
transactional_id | amount_of_purchase | Other, irrelevant columns
1 | 5000000 |
Using INNER JOIN, I've been able to get a result where I can link entity_name to either purchasing_entity or supplying_entity. And then, in the results, rather than seeing the entity_id, I get the entity name. But I want to substitute the entity name for both purchasing and supplying entity.
My ideal results would look like this:
1 [transaction ID] | Microsoft | Google | 5000000
The closes I've come is:
1 [transaction ID] | Microsoft | 2 [Supplying Entity] | 5000000
To get there, I've done:
SELECT transaction_history.transaction_id,
entity_list.entity_name,
transaction_history.supplying_entity,
transaction_details.amount_of_purchase
FROM transaction.history
INNER JOIN entity_list
ON transaction_history.purchasing_entity=entity_list.entity.id
INNER JOIN
ON transaction_history.transaction_id=transaction_details.transaction_id
I can't get entity_name to feed to both purchasing_entity and supplying_entity.
Here is the query:
SELECT h.transaction_id, h.purchasing_entity, purchaser.entity_name, h.supplying_entity, supplier.entity_name, d.amount_of_purchase
FROM transaction_history h
INNER JOIN transaction_details d
ON h.transaction_id = d.transaction_id
INNER JOIN entity_list purchaser
ON h.purchasing_entity = purchaser.entity_id
INNER JOIN entity_list supplier
ON h.supplying_entity = supplier.entity_id

SQL many-to-many select help needed

I have 2 tables
Bid_customer
|bidkey | customerkey
| 1 | 1
| 1 | 2
| 1 | 3
customer_groups
| groupkey | customerkey
| 1 | 1
| 1 | 2
| 1 | 3
What I'm trying to get is a result that will look like
| bidkey | groupkey
| 1 | 1
I've tried a cursor and joins but just don't seem to be able to get what i need any ideas or suggestions
EDIT: customers can belong to more that one group also
I am not sure who meaningful your sample data is. However following is a simple example.
Query:
select distinct b.bidkey, g.gkey
from bidcus b
inner join cusgroup g
on
b.cuskey = g.cuskey
and g.gkey = 10;
Results:
BIDKEY GKEY
1 10
Reference: SQLFIDDLE
In order to have a working Many-to-Many relationship in a database you need to have an intermediary table that defines the relationship so you do not get duplicates or mismatched values.
This select statement will join all bids with all groups because the customer matches.
Select bidkey, groupkey
From customer_groups
Inner Join bid_customer
Where customer_groups.customerkey = Bid_customer.customerkey
Hers is a sample Many to Many Relationship:
For your question:
You will need another table that joins the data. For example, GroupBids
customer_groups and bid_customer would have a one-to-many relationship with GroupBids
You would then do the following select to get your data.
Select bidkey, groupkey
From bid_customer
inner join GroupBids
ON bid_customer.primarykey = GroupBids.idBidKey
inner join customer_groups
ON customer_groups.primarykey = GroupBids.idCustomerGroupkey
This would make sure only related groups and bids are returned

Removing duplicate SQL records to permit a unique key

I have a table ('sales') in a MYSQL DB which should rightfully have had a unique constraint enforced to prevent duplicates. To first remove the dupes and set the constraint is proving a bit tricky.
Table structure (simplified):
'id (unique, autoinc)'
product_id
The goal is to enforce uniqueness for product_id. The de-duping policy I want to apply is to remove all duplicate records except the most recently created, eg: the highest id.
Or to put another way, I would like to delete only duplicate records, excluding the ids matched by the following query whilst also preserving the existing non-duped records:
select id
from sales s
inner join (select product_id,
max(id) as maxId
from sales
group by product_id
having count(product_id) > 1) groupedByProdId on s.product_id
and s.id = groupedByProdId.maxId
I've struggled with this on two fronts - writing the query to select the correct records to delete and then also the constraint in MYSQL where a subselect FROM clause of a DELETE cannot reference the same table from which data is being removed.
I checked out this answer and it seemed to deal with the subject, but seem specific to sql-server, though I wouldn't rule this question out from duplicating another.
In reply to your comment, here's a query that works in MySQL:
delete YourTable
from YourTable
inner join YourTable yt2
on YourTable.product_id = yt2.product_id
and YourTable.id < yt2.id
This would only remove duplicate rows. The inner join will filter out the latest row for each product, even if no other rows for the same product exist.
P.S. If you try to alias the table after FROM, MySQL requires you to specify the name of the database, like:
delete <DatabaseName>.yt
from YourTable yt
inner join YourTable yt2
on yt.product_id = yt2.product_id
and yt.id < yt2.id;
Perhaps use ALTER IGNORE TABLE ... ADD UNIQUE KEY.
For example:
describe sales;
+------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| product_id | int(11) | NO | | NULL | |
+------------+---------+------+-----+---------+----------------+
select * from sales;
+----+------------+
| id | product_id |
+----+------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 3 |
| 5 | 3 |
| 6 | 2 |
+----+------------+
ALTER IGNORE TABLE sales ADD UNIQUE KEY idx1(product_id), ORDER BY id DESC;
Query OK, 6 rows affected (0.03 sec)
Records: 6 Duplicates: 3 Warnings: 0
select * from sales;
+----+------------+
| id | product_id |
+----+------------+
| 6 | 2 |
| 5 | 3 |
| 2 | 1 |
+----+------------+
See this pythian post for more information.
Note that the ids end up in reverse order. I don't think this matters, since order of the ids should not matter in a database (as far as I know!). If this displeases you however, the post linked to above shows a way to solve this problem too. However, it involves creating a temporary table which requires more hard drive space than the in-place method I posted above.
I might do the following in sql-server to eliminate the duplicates:
DELETE FROM Sales
FROM Sales
INNER JOIN Sales b ON Sales.product_id = b.product_id AND Sales.id < b.id
It looks like the analogous delete statement for mysql might be:
DELETE FROM Sales
USING Sales
INNER JOIN Sales b ON Sales.product_id = b.product_id AND Sales.id < b.id
This type of problem is easier to solve with CTEs and Ranking functions, however, you should be able to do something like the following to solve your problem:
Delete Sales
Where Exists(
Select 1
From Sales As S2
Where S2.product_id = Sales.product_id
And S2.id > Sales.Id
Having Count(*) > 0
)