Update multiple records, with temporary key overlap - sql-server-2008

I've a table on DB with the following fields as PK: DOC_ID, SECTION_ID, SET_ID, CELL_ID. As you can deduce, this is referred to a set of Excel spreadsheets.
In addition to those fields, I have a CELL_ROW field and CELL_COL field.
With the SET_ID (unique), they form an alternative key.
So, al least in theory, if I want to swap two cells' coordinates, I need either to release that constraint, or use a third temporary free position (say A100000 for example).
Suppose I have the following cells:
DOC_ID | SECTION_ID | SET_ID | CELL_ID | CELL_ROW | CELL_COL
--------|------------|--------|---------|----------|----------
5 | 456 | 8778 | 15045 | 5 | 4
5 | 456 | 8778 | 15048 | 5 | 5
And suppose I have the following temporary table from which I perform an UPDATE to the main table:
DOC_ID | SECTION_ID | SET_ID | CELL_ID | CELL_ROW | CELL_COL
--------|------------|--------|---------|----------|----------
5 | 456 | 8778 | 15045 | 5 | 5
5 | 456 | 8778 | 15048 | 5 | 4
In theory, that UPDATE should raise an exception...
But just tried, it works!
Can you explain me why? Does Oracle performs it as an atomic operation, so constraints are checked only after the whole operation (instead of record per record)?
How MS SQL Server 2008 r2 and Postgres behave in this kind of situations?

PostgreSQL will throw an error, unless you defer the unique constraint (and then you can't use it as a foreign key). I am not sure about SQL Server.
As a brief aside, you probably want to call it Postgres or PostgreSQL. Calling it Postgre is a sign that you have had too little contact with the community to be corrected.
More info on PostgreSQL
PostgreSQL checks tuple constraints at tuple update time. This means that unique constraints will be violated even in cases where a set is updated in an atomic way that does not violate a unique constraint. This leads to some interesting workarounds such as multiplying an integer key by -1 for the set and then in the next update multiply by -1 again and adding one.

I tried it in Postgresql and as expected a duplicate key error is raised:
create table t (
doc_id integer,
section_id integer,
set_id integer,
cell_id integer,
cell_row integer,
cell_col integer,
primary key (doc_id, section_id, set_id, cell_id),
unique (set_id, cell_row, cell_col)
);
insert into t (doc_id, section_id, set_id, cell_id, cell_row, cell_col)
values
(5, 456, 8778, 15045, 5, 4),
(5, 456, 8778, 15048, 5, 5);
create temporary table t_temp (
doc_id integer,
section_id integer,
set_id integer,
cell_id integer,
cell_row integer,
cell_col integer
);
insert into t_temp (doc_id, section_id, set_id, cell_id, cell_row, cell_col)
values
(5, 456, 8778, 15045, 5, 5),
(5, 456, 8778, 15048, 5, 4);
update t
set
cell_col = t_temp.cell_col
from t_temp
where
t.doc_id = t_temp.doc_id
and t.section_id = t_temp.section_id
and t.set_id = t_temp.set_id
and t.cell_id = t_temp.cell_id
;
ERROR: duplicate key value violates unique constraint "t_set_id_cell_row_cell_col_key"
DETAIL: Key (set_id, cell_row, cell_col)=(8778, 5, 5) already exists.
I could do it if the constraint was set as deferrable. Check your create table statement for that keyword.

Related

search data location wise in PHP SQL

I have some parent and daughter design-wise locations-id in the MySQL database.
Where the daughter linked to the parent. I will show the database design below -
I can able to fetch the data when I search it through daughter location id wise but I don't have any idea how I combined the daughter value when I click parent location.
For example -
MainLocation (123) //total stock 23+10+56= 89
|
|
|---- DaughterLoc1 (456) //suppose stock 23
|
|---- DaughterLoc2 (789) //suppose stock 10 and total stock 10+56 = 66
|
|
|---DaughterLocA (963) //suppose stock 56
SQL : SELECT stock FROM table WHERE location = '456'
OUTPUT = 23 (Corrent)
But I want when searching location 123 I want output 89
My table design is like this below -
table: LocParent
-------------------------
| ID | stock | loc_id |
-------------------------
| 1 | 10 | 789 |
-------------------------
`location`
--------------------------------------------------------------------------------
| ID | main_loc | main_loc_id | loc_under | loc_under_id | stock |
--------------------------------------------------------------------------------
| 1 | MainLocation | 123 | DaughterLoc1 | 456 | 23 |
--------------------------------------------------------------------------------
| 2 | MainLocation | 123 | DaughterLoc2 | 789 | 10 |
--------------------------------------------------------------------------------
It is hard to tell from your sample structure what things actually look like still, and it is further complicated by multiple things called an "id". But, generally speaking, if your depth is finite, you can make small sub-queries, and if your depth is infinite (or unbound) you can make a recursive query.
Here is a sample database. It doesn't match yours, but hopefully it make sense still. If it doesn't, it would help if you provided an actual schema and data (excluding irrelevant columns).
This table is self-referencing to make things easier for demo.
CREATE TABLE sample
(
id int AUTO_INCREMENT NOT NULL,
parent_id INT NULL,
stock int NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT FOREIGN KEY (`parent_id`) REFERENCES `sample` (`id`)
);
And here's some sample data. There are two records that are "root" and don't have parent values (IDs 1 and 5), two child values (IDs 2 and 3) and one grandchild value (ID 4)
INSERT INTO sample VALUES (1, null, 11);
INSERT INTO sample VALUES (2, 1, 22);
INSERT INTO sample VALUES (3, 1, 33);
INSERT INTO sample VALUES (4, 2, 4);
INSERT INTO sample VALUES (5, null, 55);
Finite/bound
If you have a finite/bound depth, you can make use of subqueries like the below. This one goes to a depth of 3 and sums to 70. Hopefully it is fairly easy to read, but I've included a couple of comments.
SELECT
s.id,
s.stock -- root
+
(
(
SELECT
SUM(c.stock) -- child
FROM
sample c
WHERE
c.parent_id = s.id
)
+
(
SELECT
SUM(p.stock) -- grandchild
FROM
sample c
JOIN
sample p
ON
p.parent_id = c.id
WHERE
c.parent_id = s.id
)
)
as three_level_sum
FROM
sample s
WHERE
s.id = 1;
Infinite/unbound
If you have an infinite hierarchy, however, things get more complicated. MySQL and other database platforms have a thing called "Common Table Expressions" (CTEs) that allow you to make recursive queries. These can be harder to wrap your head around because of the recursion, but it basically does the same as the previous version, just with infinite depth. This version also returns the sum of 70.
WITH RECURSIVE sample_rec AS
(
SELECT
id AS root_id,
id,
parent_id,
stock
FROM
sample
WHERE
parent_id IS NULL
UNION ALL
SELECT
R.root_id,
E.id,
E.parent_id,
E.stock
FROM
sample E
INNER JOIN
sample_rec R
ON
E.parent_id = R.id
)
SELECT
SUM(stock)
FROM
sample_rec
WHERE
root_id = 1

How can I delimit the possible values of a foreign key?

I have three tables in a MySQL DB.
This is the main table with organisation related stuff. Every Organisation has an unique identifier which is also the foreign key in some tables.
org
+------------+-------------+
| org_id | name |
+------------+-------------+
| 1 | a |
| 2 | b |
| 3 | c |
+------------+-------------+
This is the groups table. Organisations can have many groups.
groups
FOREIGN KEY (ORG_ID) REFERENCES ORG (ID);
+------------+-------------+----------+
| ID | org_id | name |
+------------+-------------+ ---------+
| 1 | 1 | Group1 |
| 2 | 2 | Group2 |
| 3 | 2 | Group3 |
+------------+-------------+----------+
And this is the feed table in which I would like to perform an update.
A feed can have only one associated group.
feed
FOREIGN KEY (GROUP_ID) REFERENCES GROUPS (ID);
+------------+-------------+--------------+
| ID | org_id | group_id |
+------------+-------------+ -------------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 1 | NULL |
| 4 | 2 | 3 |
+------------+-------------+--------------+
So now there is one problem, that i can't solve. When I INSERT or UPDATE a row, I set the groups_id, but this can also be a groups_id which not belongs to the organisation.
This happens, because all ID's in GROUPS are valid FK values. That's a thing I want to avoid. It should only be possible to insert or update a row with a groups_id which has also the same org_id as in feeds.org_id.
As you can see, the data is now fine. But when I try to make this INSERT INTO feed VALUES (4, 2, 1) it were nice to see an error. Yeah, right, I'm missing an lovely error....
It is difficult for me to make an connection between them. There seems one information or method that I'm missing. I've been looking for a lot, but I don't know the words to describe my problem.
So I ask you, could you give me a tip?
EDIT:
All feeds and all groups are related to an organisation, which has an identifier. An organisation can create feeds/messages. When this feeds are not associated with a group, this feed ist public. For special feeds they can create a group. This group is related to this special organisation.
This works and everything is good:
UPDATE feed
SET title = "Title", message = "Message", groups_id = "1"
WHERE id = "1" AND org_id = "1"
But this works also:
UPDATE feed
SET title = "Title", message = "Message", groups_id = "2"
WHERE id = "1" AND org_id = "1"
The problem is, that it is possible to associate a group to a feed (which is associated to org 1), while the group is not associated with the org (group 2 is associated with org 2).
So my thought was, is there a way to solve this through FOREIGN KEY or similar (checks, joins, subqueries). Or should I think about my db design?
I think a composed foreign key solves your problem:
create table agroup (
id int primary key,
orgid int,
UNIQUE (id,orgid)
);
create table feed (
id int primary key,
groupid int,
orgid int,
FOREIGN KEY (groupid, orgid) REFERENCES agroup(id, orgid)
);
insert into agroup values (10, 1), (20, 1), (30, 2), (40, NULL);
insert into feed values (100,10,1), (101, 20, 1);
insert into feed values (102, 40, NULL); # works
insert into feed values (103, NULL, 1); # works as well
# insert into feed values (110,10,2); # yields error "Cannot add or update a child row: a foreign key constraint fails"
Note the UNIQUE(id,orgid), which seems to be necessary. Though I do not understand why agroup(id primary key) is not sufficient to make also agroup(id,orgid) unique, I got a compiler error without this explicit unique(id,orgid)-constraint. Documentation says that the referenced attributes must be indexed. Anyway, your problem should be solved.
EDIT: Extended example, which now demonstrates also the case of NULL-values in referencing attributes.
At least in MySQL, a composite foreign key constraint permits NULL values in the referencing (child) rows, regardless of whether the parent table contain rows with corresponding NULL-values or not. If one inserts a row with NULL-values for foreign-key attributes, the foreign key constraint is simply ignored. Confer mysql foreign key semantics, which says:
"... MySQL essentially implements the semantics defined by MATCH SIMPLE, which permit a foreign key to be all or partially NULL. In that case, the (child table) row containing such a foreign key is permitted to be inserted, and does not match any row in the referenced (parent) table. It is possible to implement other semantics using triggers."

SQL result table, match in second table SET type

The following two tables are not liked by any type of constraint.
First i have a table called subscription_plans that looks like this:
name | price | ID
-------------------
plan_A | 9.99 | 1
Plan_B | 19.99 | 2
plan_C | 29.99 | 3
I have a second table called pricing_offers. The subscription_plan_ID is a of type SET and can only contain values that match the ID's of the subscription_plans.ID (column from the above table). This table looks like this:
p_o_name | subscription_plan_ID | ID
-----------------------------------------
free donuts | 1 | 1
extra sauce | 1,2,3 | 2
pony ride | 3 | 3
bus fare -50% | 1,2,3 | 4
I'm trying to do a query to select everything (all fields *) from the first table and all names from the second table and the resulting rows should look like this:
name | price | p_o_name | ID
-------------------------------------------------------------
plan_A | 9.99 | free donuts, extra sauce, bus fare -50% | 1
Plan_B | 19.99 | extra_sauce, bus fare -50% | 2
plan_C | 29.99 | extra_sauce, pony ride, bus fare -50% | 3
The idea being that it should, for each row in the subscription_plans table, look ID field. Then go trough the second table and see what rows contain in the subscription_plan_ID, the ID of the row above. Gather those into a field caller p_o_name and insert its values to the matching response rows.
I tried doing this:
SELECT subscription_plans.*, pricing_offers.name
FROM subscription_plans INNER JOIN pricing_offers ON
FIND_IN_SET(subscription_plans.ID,subscription_plan_ID)
but i get instead of:
plan_A | 9.99 | free donuts, extra sauce, bus fare -50% | 1
this:
plan_A | 9.99 | free donuts | 1
plan_A | 9.99 | extra sauce | 1
plan_A | 9.99 | bus fare -50% | 1
Note: i get a response with all rows, but i just put the first one here to exemplify the difference.
Now, while i could do the processing in the response on my PHP page, i'm interested in knowing if i get the DB engine to output my desired result.
Do i need to create a type of constraint between the tables? If so how would i do it? I would be grateful for any help that would help me get to my proffered output result (even a better title for the question!).
If there are any unclear points, please let me know and i will clarify them.
Example of junction/intersect table usage.
create table subscription_plans
(
id int not null auto_increment primary key, -- common practice
name varchar(40) not null,
description varchar(255) not null,
price decimal(12,2) not null
-- additional indexes:
);
create table pricing_offers
(
id int not null auto_increment primary key, -- common practice
name varchar(40) not null,
description varchar(255) not null
-- additional indexes:
);
create table so_junction
( -- intersects mapping subscription_plans and pricing_offers
id int not null auto_increment primary key, -- common practice
subId int not null,
offerId int not null,
-- row cannot be inserted/updated if subId does not exist in parent table
-- the fk name is completely made up
-- parent row cannot be deleted and thus orphaning children
CONSTRAINT fk_soj_subplans
FOREIGN KEY (subId)
REFERENCES subscription_plans(id),
-- row cannot be inserted/updated if offerId does not exist in parent table
-- the fk name is completely made up
-- parent row cannot be deleted and thus orphaning children
CONSTRAINT fk_soj_priceoffer
FOREIGN KEY (offerId)
REFERENCES pricing_offers(id),
-- the below allows for only ONE combo of subId,offerId
CONSTRAINT soj_unique_ids unique (subId,offerId)
-- additional indexes:
);
insert into subscription_plans (name,description,price) values ('plan_A','description',9.99);
insert into subscription_plans (name,description,price) values ('plan_B','description',19.99);
insert into subscription_plans (name,description,price) values ('plan_C','description',29.99);
select * from subscription_plans;
insert into pricing_offers (name,description) values ('free donuts','you get free donuts, limit 3');
insert into pricing_offers (name,description) values ('extra sauce','extra sauce');
insert into pricing_offers (name,description) values ('poney ride','Free ride on Wilbur');
insert into pricing_offers (name,description) values ('bus fare -50%','domestic less 50');
select * from pricing_offers;
insert so_junction(subId,offerId) values (1,1); -- free donuts to plans
insert so_junction(subId,offerId) values (1,2),(2,2),(3,2); -- extra sauce to plans
insert so_junction(subId,offerId) values (3,3); -- wilbur
insert so_junction(subId,offerId) values (1,4),(2,4),(3,4); -- bus to plans
select * from so_junction;
-- try to add another of like above to so_junction
-- Error Code 1062: Duplicate entry
-- show joins of all
select s.*,p.*
from subscription_plans s
join so_junction so
on so.subId=s.id
join pricing_offers p
on p.id=so.offerId
order by s.name,p.name
-- show extra sauce intersects
select s.*,p.*
from subscription_plans s
join so_junction so
on so.subId=s.id
join pricing_offers p
on p.id=so.offerId
where p.name='extra sauce'
order by s.name,p.name
Basically you insert and delete from the junction table (no good really updating ever in this example).
Clean and fast joins without having to mess with slow, unwieldy sets without indexes
No one can ride the Wilbur the Poney anymore? Then
delete from so_junction
where offerId in (select id from pricing_offers where name='poney ride')
Ask if you have any questions.
And good luck!

update if two fields exists, insert if not (MySQL)

This isn't an (exact) duplicate of this questions so I'Ve started a new one.
I have this table (ID is primary and auto increment)
ID | mykey | myfoo | mybar
============================
1 | 1.1 | abc | 123
2 | 1.1.1 | def | 456
3 | 1.2 | abc | 789
4 | 1.1 | ghi | 999
I would like to UPDATE row 1 with mybar = "333" only if mykey = '1.1' AND myfoo = 'abc'
If either mykey != '1.1' OR myfoo != 'abc' I would like to INSERT an new row.
Is this possible with one statement?
A unique index in MySQL does not have to be on a single column. You can add a UNIQUE index on multiple columns simply by specifying more columns in your ALTER TABLE..ADD UNIQUE statement:
ALTER TABLE myTable ADD UNIQUE (
mykey,
myfoo
);
Now you can use a regular INSERT INTO...ON DUPLICATE KEY statement.
SQLFiddle DEMO (note that the multiple repeated values are not added - all others are)
Note:
If either is NULL, it will not be counted as unique. mykey being 'bar' and myfoo being NULL could be added to infinity even though they have the "same" values (NULL isn't really a value).

MYSQL: Querying from tables based upon Len Silverston's "The Data Model Resource Book"

I am currently in the process of developing a Database software for a company that I am working with. I based the tables off of Len Silverston's book, as I found it to be an excellent source for information based on data modeling.
Now, you do not need to be acquainted with his book to know the solution to my problem, but I could not think of any other way to word my title.
Suppose I have two tables, Persons and Person_Names:
CREATE TABLE Persons
(
party_id INT PRIMARY KEY,
birth_date DATE,
social VARCHAR(20)
);
CREATE TABLE Person_Names
(
party_id INT,
name_id INT,
person_name VARCHAR(20),
CONSTRAINT person_names_cpk
PRIMARY KEY (party_id, name_id)
);
The two tables can be joined by party_id. Also, under Person_Names, name_id = 1 correlates to the person's first name (which is stored in the field person_name) and name_id = 2 is the person's last name.
* EDIT *
Someone asked for some data, so I will add some data below:
INSERT INTO Persons VALUES
(1, '01-01-1981', '111-11-1111'),
(2, '02-02-1982', '222-22-2222'),
(3, '03-03-1983', '333-33-3333');
INSERT INTO Person_Names VALUES
(1, 1, 'Kobe'),
(1, 2, 'Bryant'),
(2, 1, 'LeBron'),
(2, 2, 'James'),
(3, 1, 'Kevin'),
(3, 2, 'Durant');
Now that I added those data, how would I query the following?
-----------------------------------------------------------------------
| Party Id | First Name | Last Name | Birthdate | Social No. |
-----------------------------------------------------------------------
| 1 | Kobe | Bryant | 01-01-1981 | 111-11-1111 |
| 2 | LeBron | James | 02-02-1982 | 222-22-2222 |
| 3 | Kevin | Durant | 03-03-1983 | 333-33-3333 |
-----------------------------------------------------------------------
Thanks for taking your time to read my question!
Quite easily. I don't know the book, but presumably it contains some material describing table joins and their application in queries such as this one:
SELECT Persons.party_id AS "Party Id",
firstname.person_name AS "First Name",
lastname.person_name AS "Last Name",
Persons.birth_date AS "Birthdate",
Persons.social AS "Social No."
FROM Persons
INNER JOIN Person_Names firstname
ON Persons.party_id = firstname.party_id
AND firstname.name_id = 1
INNER JOIN Person_Names lastname
ON Persons.party_id = lastname.party_id
AND lastname.name_id = 2
Be advised that this will return results only for those people who have both a first and a last name defined in your Person_Names table; if one or the other isn't present, the INNER JOINs' ON clause conditions will exclude those rows entirely.