It is not so simple as it is written in the title.
I have a table, for binding user accounts together.
For example: a person signs up via standard site functionality. On the next day he signs up via the Facebook account.
I have user_user table with a_user_id, b_user_id fields. These fields are foreign keys to the standard user table.
I make unique key for 2 fields.
But. How to avoid the situation like this one:
a_user_id: 1
b_user_id : 2
and the next row:
a_user_id: 2
b_user_id : 1
Both rows mean the same. Can MySQL handle this?
Are there any better approaches for this case?
Introduce the constraint a_user_id < b_user_id. Honestly said, I till now always held this constraint on the programming side, This prevents cycles: self references and a-b-a.
First one would need to normalize the table, probably using a temporary table in MySQL.
Normalisation of the old table:
Assuming a table u2(a_user_id NOT NULL, b_user_id NOT NULL):
DELETE FROM u2
WHERE a_user_id = b_user_id;
ALTER TABLE u2 ADD COLUMN other ...;
UPDATE u2
SET
other = a_user_id,
a_user_id = b_user_id,
b_user_id = other
WHERE
a_user_id > b_user_id;
ALTER TABLE u2 REMOVE COLUMN other;
(Unchecked syntax)
Related
table 1 is called (athlete) and table2 is called (training_session.id) the primary key to table 1 is ID, and the table 2 has the primary key Athelete_id
I want to delete a person from my database by using his name, which I've called "Pet". However, he is also connected to another table which stores his training session. So (ID 1) on table 1 is connected to table 2 (athlete id1)
I struggle a lot, I try using INNER JOIN.
DELETE athlete,training_session FROM athlete
INNER JOIN
training_session ON training_session.id = athlete.name
WHERE
athlete.name = "Pet;
I have something wrong with my syntax, is it correct to use Inner Join or have I misunderstood
You should have set up foreign key constraints with Cascade deletions to simplify the logic and all you would have needed than was to delete from athlete. So I would suggest you add it.
For more info you can take a look at:
http://www.mysqltutorial.org/mysql-on-delete-cascade/
I have a address table which is referenced from 6 other tables (sometimes multiple tables). Some of those tables have around half a million records (and the address table around 750000 records). I want to have a periodical query running which deletes all records that are not referenced from any of the tables.
The following sub-queries is not a option, because the query never finishes - the scope is too big.
delete from address where address_id not in (select ...)
and not in (select ...) and not in (select ...) ...
What I was hoping was that I could use the foreign key constraint and I could simply delete all records for which the foreign key constraint does not stop me (because there is no reference to the table). I could not find a way to do this (or is there?). Anybody another good idea to tackle this problem?
You can try this ways
DELETE
address
FROM
address
LEFT JOIN other_table ON (address.id = other_table.ref_field)
LEFT JOIN other_table ON (address.id = other_table2.ref_field)
WHERE
other_table.id IS NULL AND other_table2.id IS NULL
OR
DELETE
FROM address A
WHERE NOT EXISTS (
SELECT 1
FROM other_table B
WHERE B.a_key = A.id
)
I always use this:
DELETE FROM table WHERE id NOT IN (SELECT id FROM OTHER table)
I'd do this by first creating a TEMPORARY TABLE (t) that is a UNION of the IDs in the 6 referencing tables, then run:
DELETE x FROM x LEFT JOIN t USING (ID) WHERE x.ID IS NULL;
Where x is the address table.
See 'Multiple-table syntax' here:
http://dev.mysql.com/doc/refman/5.0/en/delete.html
Obviously, your temporary table should have its PRIMARY KEY on ID. It may take some time to query and join, but I can't see a way round it. It should be optimized, unlike the multiple sub-query version.
I have multiple tables holding infos about users and content.
Table users:
id name
5 foo
33 abc
Table imgs:
id uid img_name
1 5 bar
8 33 xy
Table user_permissions:
id uid permission_id
1 5 3
2 3 3
Table liked_content:
id contente_id content_holder_id likedby_id
1 8 33 5
2 1 5 56
If a user is deleted I want to delete all rows in all tables related to his user-id. This works fine if I add only one column per table.
DELETE users,
imgs,
user-permissions,
liked_content
FROM users,
imgs,
user-permissions,
liked_content
WHERE imgs.uid = users.id AND
user_permissions.uid = users.id AND
liked_content.content_holder_id = users.id AND
users.id = 5
Adding a second row in the WHERE-clause (likedby_id/liked_content-table) where the id can be found will not work.
...
WHERE imgs.uid = users.id AND
user_permissions.uid = users.id AND
liked_content.content_holder_id = users.id AND
liked_content.likedby_id = users.id AND
users.id = 5
What would be the correct way adding a second column to the query for a table already listed?
According to your data, user id can be in liked_content table as either content_holder_id OR likedby_id. Not necessarily as both. Where clause should change as:
...
WHERE imgs.uid = users.id AND
user_permissions.uid = users.id AND
(liked_content.content_holder_id = users.id OR
liked_content.likedby_id = users.id) AND
users.id = 5
If you use foreign key constraint along with cascade delete rule, you do not have to do this manually.
What you're implementing yourself is a cascading delete. It is generally built into the DB engine. Check out this related question: MySQL foreign key constraints, cascade delete
If you were dead set on doing it yourself, I'd recommend multiple delete statements within a transaction that traverse your schema from child to parent:
DELETE
FROM liked_content
WHERE content_holder_id = 5;
DELETE
FROM imgs
WHERE uid = 5;
DELETE
FROM user-permissions
WHERE uid = 5;
DELETE
FROM users
WHERE id = 5;
The technically correct way, as others have stated, is to setup cascading deletes.
I would not willingly do that if I was in charge of the database, simply because you will find it to be very painful trying to manipulate the tables and especially when you are trying to create sample data.
I would go for the KISS method...
Do a DELETE for each subordinate table individually referring to the users id, then delete the master record.
If you do the foreign key and cascading thing, you will not like what it does to your content table, since it has two relations to the master user table.
It means, for example, if you are attempting to delete content rows for holder_id 5, you will first have to disconnect those rows from their associated likedby_id master record by setting the likedby_id to NULL, otherwise the cascade would delete the likedby_id master record. But, it also won't allow you to NULL the likedby_id because doing so would invalidate the foreign key relations you need to actually do the cascade.
Do yourself a favor - Go simple.
I'm new to SQL and I'm having a hard time figuring out how to execute queries with foreign keys on MySQL Workbench.
In my example, I have three tables: people, places, and people_places.
In people, the primary key is people_id and there's a column called name with someone's name.
In places, the primary key is places_id and there's a column called placename with the name of a place.
People_places is a junction table with three columns: idpeople_places (primary key), people_id (foreign key), and places_id (foreign key). So this table relates a person to a place using their numerical IDs from the other two tables.
Say I want the names of everyone associated with place #3. So the people_places table has those associations by number, and the people table relates those numbers back to the actual names I want.
How would I execute that query?
Try this to find all the people names who are associated with place id 3.
SELECT p.name
FROM people as p
INNER JOIN people_places as pp on pp.people_id = p.people_id
WHERE pp.places_id = 3
OK, so you need to "stitch" all three tables together, yeah?
Something like this:
select people.name
from people -- 1. I like to start with the table(s) that I want data from, and
, people_places -- 2. then the "joining" table(s), and
, places -- 3. finally the table(s) used "just" for filtering.
where people.people_id = people_places.people_id -- join table 1 to table 2
and people_places.place_id = places.place_id -- join table 2 to table 3
and places.name = "BERMUDA" -- restrict rows in table 3
I'm sure you can do the rest.
Cheers. Keith.
I have four Database Tables like these:
Book
ID_Book |ID_Company|Description
BookExtension
ID_BookExtension | ID_Book| ID_Discount
Discount
ID_Discount | Description | ID_Company
Company
ID_Company | Description
Any BookExtension record via foreign keys points indirectly to two different ID_Company fields:
BookExtension.ID_Book references a Book record that contains a Book.ID_Company
BookExtension.ID_Discount references a Discount record that contains a Discount.ID_Company
Is it possible to enforce in Sql Server that any new record in BookExtension must have Book.ID_Company = Discount.ID_Company ?
In a nutshell I want that the following Query must return 0 record!
SELECT count(*) from BookExtension
INNER JOIN Book ON BookExstension.ID_Book = Book.ID_Book
INNER JOIN Discount ON BookExstension.ID_Discount = Discount.ID_Discount
WHERE Book.ID_Company <> Discount.ID_Company
or, in plain English:
I don't want that a BookExtension record references a Book record of a Company and a Discount record of another different Company!
Unless I've misunderstood your intent, the general form of the SQL statement you'd use is
ALTER TABLE FooExtension
ADD CONSTRAINT your-constraint-name
CHECK (ID_Foo = ID_Bar);
That assumes existing data already conforms to the new constraint. If existing data doesn't conform, you can either fix the data (assuming it needs fixing), or you can limit the scope (probably) of the new constraint by also checking the value of ID_FooExtension. (Assuming you can identify "new" rows by the value of ID_FooExtension.)
Later . . .
Thanks, I did indeed misunderstand your situation.
As far as I know, you can't enforce that constraint the way you want to in SQL Server, because it doesn't allow SELECT queries within a CHECK constraint. (I might be wrong about that in SQL Server 2008.) A common workaround is to wrap a SELECT query in a function, and call the function, but that's not reliable according to what I've learned.
You can do this, though.
Create a UNIQUE constraint on Book
(ID_Book, ID_Company). Part of it will look like UNIQUE (ID_Book, ID_Company).
Create a UNIQUE constraint on Discount (ID_Discount, ID_Company).
Add two columns to
BookExtension--Book_ID_Company and
Discount_ID_Company.
Populate those new columns.
Change the foreign key constraints
in BookExtension. You want
BookExtension (ID_Book,
Book_ID_Company) to reference
Book (ID_Book, ID_Company). Similar change for the foreign key
referencing Discount.
Now you can add a check constraint to guarantee that BookExtension.Book_ID_Company is the same as BookExtension.Discount_ID_Company.
I'm not sure how [in]efficient this would be but you could also use an indexed view to achieve this. It needs a helper table with 2 rows as CTEs and UNION are not allowed in indexed views.
CREATE TABLE dbo.TwoNums
(
Num int primary key
)
INSERT INTO TwoNums SELECT 1 UNION ALL SELECT 2
Then the view definition
CREATE VIEW dbo.ConstraintView
WITH SCHEMABINDING
AS
SELECT 1 AS Col FROM dbo.BookExtension
INNER JOIN dbo.Book ON dbo.BookExtension.ID_Book = Book.ID_Book
INNER JOIN dbo.Discount ON dbo.BookExtension.ID_Discount = Discount.ID_Discount
INNER JOIN dbo.TwoNums ON Num = Num
WHERE dbo.Book.ID_Company <> dbo.Discount.ID_Company
And a unique index on the View
CREATE UNIQUE CLUSTERED INDEX [uix] ON [dbo].[ConstraintView]([Col] ASC)