The effect of index on unique constraint columns - database

How is the table made?
create table market_post
(
.
.
.
d_id varchar(20) constraint unique_d_id unique,
.
.
.
);
create index market_post_d_i_219a22_idx on market_post (d_id, is_deleted);
It should be noted that above code is DDL of table and i created the indexes and unique constraint when the table was created and was full of data (ALTER....)
Sometimes it allows duplicate value in d_id and sometimes it not allows!!
Let's test:
TEST1
SELECT id,d_id
FROM public.market_post
WHERE id in (1910764,2584556)
Result:
--------------------------------
| id | d_id |is_deleted|
--------------------------------
|1910764 | QYynk1fG | true |
--------------------------------
|2584556 | gYkgfj_M | true |
--------------------------------
now i want update:
UPDATE public.market_post SET d_id = 'gYkgfj_M'WHERE id = 1910764
Result:
[2022-07-24 10:31:52] 1 row affected in 116 ms
OMG! now result is:
---------------------
| id | d_id |
---------------------
|1910764 | gYkgfj_M |
---------------------
|2584556 | gYkgfj_M |
---------------------
interesting point
SELECT id,d_id FROM public.market_post WHERE d_id='gYkgfj_M'
only returnt one row !!!!!!!!
---------------------
| id | d_id |
---------------------
|1910764 | gYkgfj_M |
---------------------
TEST2
SELECT id,d_id
FROM public.market_post
WHERE id in (191076 , 258455)
Result:
--------------------------------
| id | d_id |is_deleted|
--------------------------------
|191076 | SYyFk1fA | false |
--------------------------------
|258455 | fYkDfjbb | false |
--------------------------------
now i want update:
UPDATE public.market_post SET d_id = 'fYkDfjbb' WHERE id = 191076
Result:
[23505] ERROR: duplicate key value violates unique constraint "unique_d_id"
Detail: Key (d_id)=(fYkDfjbb) already exists.
its guarantees that the duplicate value was not found in the rows where is_deleted=false
Unique constraint does not work in Postgres?(Of course it should work) Or has the index affected it?
is this bug? no , i tested it in new table (in my server and in SQL fiddle) and all of them work truly , and there isn't any bug
But the old table is not work
It should be noted that I created the indexes and unique constraint when the table was full of data
VAERSION:12

As #ErwinBrandstetter said in the comments, I should have rebuilt the indexes! Apparently, I had fallen into a Postgres bug
After running ‍‍‍‍REINDEX TABLE market_post; everything was solved
The hard part was where I had to distinguish values with duplicate IDs and delete one of them! As I said in the question, Postgres returns only one of the records. I took the entire table with the help of pandas and identified the duplicate values and then removed them
After the steps, it was time to REINDEX

Related

Database Design - Referencing a table multiple times from multiple tables

Hi stackoverflow database design experts!
I'm facing a design problem in my database, and I've not found any similar issue in Stackoverflow, hence this question.
I have an image table, containing image data and it's primary key. In my design, each image can be referenced multiple time accross multiple tables.
Here is a representation of the database:
-------------------- -------------------------------------------
| image | | table1 |
|--------------------| |-------------------------------------------|
| id_image | data | | id_table1 | id_image | data |
|----------|---------| |-----------|----------|--------------------|
| 1 | Image 1 | | 1 | 1 | References image 1 |
| 2 | Image 2 | | 2 | 3 | References image 3 |
| 3 | Image 3 | -------------------------------------------
--------------------
-------------------------------------------
| table2 |
|-------------------------------------------|
| id_table2 | id_image | data |
|-----------|----------|--------------------|
| 1 | 2 | References image 2 |
| 2 | 2 | References image 2 |
| 3 | 3 | References image 3 |
-------------------------------------------
Here are the tables detail:
image table
id_image auto-incremented primary key
data image data
table1 table
id_table1 auto-incremented primary key
id_image foreign key referencing image.id_image
data table1 data
table2 table
id_table2 auto-incremented primary key
id_image foreign key referencing image.id_image
data table2 data
I want my database to behave as follows:
If I delete the table1 row with id_table1 = 1, the image row with id_image = 1 must be deleted (no other references to this image)
If I then delete the table2 row with id_table2 = 1, no image should be deleted (because the image with id_image = 2 is still referenced by the table2 row with id_table2 = 2)
If I then delete the table2 row with id_table2 = 2, the image row with id_image = 2 must be deleted (no other references to this image)
If I then delete the table1 row with id_table1 = 2, no image should be deleted (because the image with id_image = 3 is still referenced by the table2 row with id_table2 = 3)
If I then delete the table2 row with id_table2 = 3, the image row with id_image = 3 must be deleted (no other references to this image)
I've already tried some cascading delete, by inverting the foreign keys (i.e. image table containing id_table1 and id_table2 foreign keys), but if an image is referenced in 2 other tables, removing one referenced table entry also removes the image, which i do not want to happen.
I've also tried to define triggers, but this approach is more complex than I thought: each time I have to check among all foreign keys to id_image to see if there is another reference to the image to delete. This sample contains 2 foreign keys, but in the database I'm designing there will be more than 10...
I feel like there is a simple solution to this simple problem, anyone here to help me?
Thanks!
Right away because of your first requirement:
If I delete the table1 row with id_table1 = 1, the image row with
id_image = 1 must be deleted (no other references to this image)
I can tell you that you can only accomplish this with a TRIGGER. The reason is because you want to automatically delete from the Parent table when a row is deleted from the Child table.
The reverse (Delete child when parent is deleted) can be done with Cascading Foreign Keys, but not this.
You will need to put Triggers on both child tables to enforce the logic you want.
I came up with a better design yesterday. It still uses triggers (as Tab Alleman said), but those are much simplier to define:
-------------------- ---------------------------
| image | | image_proxy |
|--------------------| |---------------------------|
| id_image | data | | id_image_proxy | id_image |
|----------|---------| |----------------|----------|
| 1 | Image 1 | | 1 | 1 |
| 2 | Image 2 | | 2 | 3 |
| 3 | Image 3 | | 3 | 2 |
-------------------- | 4 | 2 |
| 5 | 3 |
---------------------------
-------------------------------------------------
| table1 |
|-------------------------------------------------|
| id_table1 | id_image_proxy | data |
|-----------|----------------|--------------------|
| 1 | 1 | References image 1 |
| 3 | 2 | References image 3 |
-------------------------------------------------
-------------------------------------------------
| table2 |
|-------------------------------------------------|
| id_table2 | id_image_proxy | data |
|-----------|----------------|--------------------|
| 1 | 3 | References image 2 |
| 2 | 4 | References image 2 |
| 3 | 5 | References image 3 |
-------------------------------------------------
As you can see in the schema above, I've introduced a new table: image_proxy:
id_image_proxy auto-incremented primary key
id_image foreign key referencing image.id_image
Also, table1 and table2 reference now an image_proxy entry instead of an image entry.
With this design, the triggers are now:
After deleting entries in table1, delete corresponding entries in image_proxy.
After deleting entries in table2, delete corresponding entries in image_proxy.
After deleting entries in image_proxy, delete entries in image that are not referenced anymore in image_proxy.
I don't know if this design is the best for this issue, nor if triggers usage is safe, that's why I'll keep an eye on this post if there is any better answer or relevent comment!

Oracle 11g Insert into from another table that has duplicates

Ok so I have 2 new tables: Client and Contract. I'm gonna focus on the first one as they have the same structure. Client looks like:
+-----------+---------+
| client_id | name |
+-----------+---------+
| Value 1 | Value 2 |
+-----------+---------+
And created like this:
CREATE TABLE Client (
client_id varchar2(15) NOT NULL,
name varchar2(100) NOT NULL,
CONSTRAINT Client_pk PRIMARY KEY (client_id)
) ;
Also I have an old table: old_contracts looking like:
+------------+----------+------+
| contractid | clientid | name |
+------------+----------+------+
| con1 | cli1 | n1 |
| con2 | cli2 | n2 |
| con3 | cli2 | n2 |
| con4 | cli3 | n3 |
| con5 | cli3 | n3 |
+------------+----------+------+
Defined:
CREATE TABLE old_contracts(
contractid varchar2(15) NOT NULL
clientid varchar2(15) NOT NULL,
name varchar2(100) NOT NULL
) ;
I want to take the data from old_contract and insert it into Client.
This old_contracts table has rows with duplicate clientid (one client can have more than one contract) but I don't want to have duplicates on Client table so I am doing this:
INSERT INTO Client (
client_id,
name
) SELECT DISTINCT
clientid,
name
FROM old_contracts;
to not get duplicates. Anyway, I'm getting this error:
Error SQL: ORA-00001: unique constraint (USER.CLIENT_PK) violated
00001.00000 - "unique constraint (%s.%s) violated"
What's going on? I believe the DISTINCT keyword was going to do the thing.
I've also tried adding a WHERE NOT EXISTS clause as suggested in related posts (i.e. this one), but the result I'm getting is the same error.
Most likely, the name is not always the same for a given clientid.
Try this instead:
INSERT INTO Client (
client_id,
name
) SELECT clientid,
max(name)
FROM old_contracts
GROUP BY clientid;

SQL Server Indexes - Column Order

Going of the diagram here: I'm confused on column 1 and 3.
I am working on an datawarehouse table and there are two columns that are used as a key that gets you the primary key.
The first column is the source system. there are three possible values Lets say IBM, SQL, ORACLE. Then the second part of the composite key is the transaction ID it could ne numerical or varchar. There is no 3rd column. Other than the secret key which would be a key generated by Identity(1,1) as the record gets loaded. So in the graph below I imagine if I pass in a query
Select a.Patient,
b.Source System,
b.TransactionID
from Patient A
right join Transactions B
on A.sourceSystem = B.sourceSystem and
a.transactionID = B.transactionID
where SourceSystem = "SQL"
The graph leads me to think that column 1 in the index should be set to the SourceSystem. Since it would immediately split the drill down into the next level of index by a 3rd. But when showing this graph to a coworker, they interpreted it as column 1 would be the transactionID, and column 2 as the source system.
Cols
1 2 3
-------------
| | 1 | |
| A |---| |
| | 2 | |
|---|---| |
| | | |
| | 1 | 9 |
| B | | |
| |---| |
| | 2 | |
| |---| |
| | 3 | |
|---|---| |
First, you should qualify all column names in a query. Second, left join usually makes more sense than a right join (the semantics are keep all columns in the first table). Finally, if you have proper foreign key relationships, then you probably don't need an outer join at all.
Let's consider this query:
Select p.Patient, t.Source System, t.TransactionID
from Patient p join
Transactions t
on t.sourceSystem = p.sourceSystem and
t.transactionID = p.transactionID
where t.SourceSystem = 'SQL';
The correct index for this query is Transactions(SourceSystem, TransactionId).
Notes:
Outer joins affect the choice of indexes. Basically if one of the tables has to be scanned anyway, then an index might be less useful.
t.SourceSystem = 'SQL' and p.SourceSystem = 'SQL' would probably optimize differently.
Does the patient really have a transaction id? That seems strange.

TSQL procedural issue and advice

I am looking for advice on the best way to accomplish the following
I have a table in SQL Server that holds downloaded data from an external system. I need to use it to update another database. Some records will be inserts and others will be updates. There is a comment table and a main table to insert/update. Comments are linked by an ID created in the comments table and stored in a column of the main table record. (one to one relationship)
So insert into comment table and get a scope_identity return value and then use that as part of the insert statement for the main table.
The updates get the comment ID from the record in the main table and then update the comment table where necessary and also the main table where necessary
EG Table has 5 records
Get first record
If exists in database
get commentID column from comment table and update comment and main table
If not exists
insert into comment table and return comment ID and insert the record into the main table with that comment ID
get the next record
I'm struggling to figure out how to best do this in SQL Server. Can't find the right combination of cursor, while loops, stored procedure etc. Haven't done much by way of procedural work in SQL Server.
Any advice/help is greatly appreciated
Thanks
Habo. I appreciate the feedback. I do struggle to write a clear concise question. The linked page provides good advice. Hope this script below helps clarify.
Thanks again.
USE TEMPDB
--TABLE TO HOLD JOB RECORDS
create table tbl_jobs(
jobnumber varchar(16) primary key clustered,
jobdesc varchar(50),
commentID int
)
GO
INSERT INTO tbl_jobs VALUES ('Job1','Desc1', '1')
INSERT INTO tbl_jobs VALUES ('Job2','Desc2', '2')
INSERT INTO tbl_jobs VALUES ('Job3','Desc3', '3')
--TABLE TO HOLD JOB RECORD COMMENTS
create table tbl_jobComments(
commentID INT IDENTITY(1,1) NOT NULL,
comment text
)
GO
Insert into tbl_jobComments VALUES ('Comment1')
Insert into tbl_jobComments VALUES ('Comment2')
Insert into tbl_jobComments VALUES ('Comment3')
--TABLE TO HOLD RECORDS DOWNLOADED FROM EXTERNAL SYSTEM
create table tbl_updates(
jobnumber varchar(16) primary key clustered,
jobdesc varchar(50),
comment text
)
GO
INSERT INTO tbl_updates VALUES ('Job1','Desc1Modified', 'Comment1')
INSERT INTO tbl_updates VALUES ('Job2','Desc2', 'Comment2')
INSERT INTO tbl_updates VALUES ('Job3','Desc3Modified', 'Comment3')
INSERT INTO tbl_updates VALUES ('Job4','Desc4', 'Comment4')
GO
--OUTPUT FROM tbl_Jobs
+-----------+---------+-----------+
| jobnumber | jobdesc | commentID |
+-----------+---------+-----------+
| Job1 | Desc1 | 1 |
| Job2 | Desc2 | 2 |
| Job3 | Desc3 | 3 |
+-----------+---------+-----------+
--OUTPUT FROM tbl_JobComments
+-----------+----------+
| commentID | comment |
+-----------+----------+
| 1 | Comment1 |
| 2 | Comment2 |
| 3 | Comment3 |
+-----------+----------+
--OUTPUT FROM tbl_updates
+-----------+---------------+-----------+
| jobnumber | jobdesc | comment |
+-----------+---------------+-----------+
| Job1 | Desc1Modified | Comment1 |
| Job2 | Desc2 | Comment2a |
| Job3 | Desc3Modified | Comment3 |
| Job4 | Desc4 | Comment4 |
+-----------+---------------+-----------+
--DESIRED RESULTS tbl_jobs
+-----------+-----------------+-----------+
| jobnumber | jobdesc | commentID |
+-----------+-----------------+-----------+
| Job1 | Desc1Modified | 1 |
| Job2 | Desc2 | 2 |
| Job3 | Desc3Modified | 3 |
| Job4 | Desc4 | 4 |
+-----------+---------+-------------------+
--DESIRED RESULTS tbl_jobs_comments
+-----------+-----------+
| commentID | comment |
+-----------+-----------+
| 1 | Comment1 |
| 2 | Comment2a |
| 3 | Comment3 |
| 4 | Comment4 |
+-----------+-----------+
You can break this into 2 statements, an update and an insert query
(This assumes there is only 1 comment per ID)
UPDATE maintable
SET Comment=upd.comment
FROM maintable mt
JOIN updatestable upd
ON mt.id=upd.id
then insert what is missing:
INSERT INTO maintable (id,comment)
SELECT id, comment
FROM updatestable
WHERE id NOT IN (SELECT id FROM maintable)

SQL Server : Insert Multiple Rows into Multiple Tables From Table Type Paramater

I'm trying to write a stored procedure which takes in a table type parameter and inserts into two tables at once.
I have an entity table which is a base table holding the id for various tables, below is the entity table and a sample Site table.
------ Entity Table ------------------------------------------
| Id | bigint | NOT NULL | IDENTITY(1,1) | PRIMARY KEY
| TypeId | tinyint | NOT NULL |
| Updated | datetime | NULL |
| Created | datetime | NOT NULL |
| IsActive | bit | NOT NULL |
------- Site Table ---------------------------------------
| EntityId | bigint | NOT NULL | PRIMARY KEY
| ProductTypeCode | nvarchar(8) | NOT NULL | PRIMARY KEY
| SupplierCode | nvarchar(8) | NOT NULL | PRIMARY KEY
| Name | nvarchar(128) | NOT NULL |
| Description | nvarchar(max) | NULL |
And here is my table type used to pass into the stored procedure
------- Site Table Type ----------------------------------
| EntityTypeId | tinyint | NOT NULL |
| ProductTypeCode | nvarchar(8) | NOT NULL | PRIMARY KEY
| SupplierCode | nvarchar(8) | NOT NULL | PRIMARY KEY
| Name | nvarchar(128) | NOT NULL |
| Description | nvarchar(max) | NULL |
The idea is that I will pass in a table type parameter into the stored procedure and insert multiple rows at once to save looping inserting one row at a time.
Here's what I have so far
CREATE PROCEDURE InsertSites
#Sites SiteTypeTable READONLY
AS
BEGIN
-- Insert into Entity & Site Tables here, using the Id from the Entity Table in the Site table
INSERT INTO Entity (TypeId, Updated, Created, IsActive)
OUTPUT [inserted].[Id], S.ProductTypeCode, S.SupplierCode, S.Name, S.Description
INTO Site
SELECT EntityTypeId, NULL, GETDATE(), 1
FROM #Sites S
END
I've read about using insert and output together but cannot get this to work. I've also read about merge but also cannot get this to work.
Any help or pointers you can give will be greatly appreciated.
Thanks
Neil
---- Edit ----
Could I do something like this? I'm not sure how to finish this off...
CREATE PROCEDURE InsertSites
#Sites SiteTypeTable READONLY
AS
BEGIN
-- First insert enough rows into Entity table, saving the inserted Ids to a table variable
DECLARE #InsertedOutput TABLE (EntityId bigint)
INSERT INTO Entity (TypeId, Updated, Created, IsActive)
OUTPUT [inserted].[id]
INTO #InsertedOutput
SELECT EntityTypeId, NULL, GETDATE(), 1
FROM #Sites S
-- Use the Ids in #InsertedOutput against the rows in #Sites to insert into Sites
END

Resources