Composite Key join in SQL Error - sql

I an trying to join two tables with two keys. The join code is like:
select
col1, col2, col3, col4
from
data a
join
data1 b on a.col1 = b.col1 and a.col3 = b.col3
I am getting error:
Query Error: Error: ER_NON_UNIQ_ERROR: Column 'col1' in field list is ambiguous
CREATE TABLE data
(
Id Serial,
col1 VARCHAR(70) NOT NULL,
col3 varchar(70),
col2 integer,
PRIMARY KEY(Id)
);
CREATE TABLE data1
(
Id Serial,
col1 VARCHAR(70) NOT NULL,
col3 varchar(70),
col4 integer,
PRIMARY KEY(Id)
);
SQL fiddle with sample data

It's ambiguous because postgres wants to know which table you want col1 from.
Add an alias to the fields in the select clause. E.g. select a.col1...

Related

How to select more columns than insert column

I have the following query:
INSERT INTO TableA (Col1, Col2, Col3)
OUTPUT #SomeData, INSERTED.ID, ID INTO TableB(SomeColumn, TableAID, ID)
SELECT Col1, Col2, Col3, ID
FROM TableC;
When I run it, I get this error:
The select list for the INSERT statement contains more items than the insert list. The number of SELECT values must match the number of INSERT columns.
That error makes sense, but I don't know how to fix it. I want to select 4 columns from TableC, but I want to only insert three of them (Col1, Col2, Col3) into TableA. I am selecting the column ID because I want to insert it into the ID column of TableB. Is there any way to do that?
CREATE TABLE TableA
(
ID bigint identity
constraint PK_TableA_ID
primary key
Col1 int,
Col2 int,
Col3 int
)
CREATE TABLE TableB
(
ID [int] NOT NULL,
SomeColumn [int] NOT NULL,
TableAID [bigint] NOT NULL
);
CREATE TABLE TableC
(
ID [int] IDENTITY (1,1) NOT NULL,
Col1 [int],
Col2 [int],
Col3 [int],
);
You can try merge and write something like this:
MERGE TableA AS target
USING TableC AS source
ON 1 = 0
WHEN NOT MATCHED BY target
THEN INSERT (Col1, Col2, Col3) VALUES (source.Col1, source.Col2, source.Col3)
OUTPUT #SomeData, INSERTED.ID, source.ID INTO TableB (SomeColumn,TableAID, ID);

how we can Create Primary Keys in a table

How can I create a Primary Key in a table. I used the following command to create one, but the key doesn't appear to be enforced:
ALTER TABLE tablename ADD PRIMARY KEY (column1,column3);
Snowflake supports defining and maintaining constraints, but does not enforce them, except for NOT NULL constraints, which are always enforced.
https://docs.snowflake.com/en/sql-reference/constraints-overview.html#supported-constraint-types
Although the primary key is not enforced in Snowflake, you can use the MERGE statement to insert the data to enforce uniqueness of that key. This approach requires making sure that the source data set does not include duplicates, by using for example QUALIFY function.
Example:
CREATE TABLE IF NOT EXISTS test (
col1 VARCHAR(50) NOT NULL,
col2 VARCHAR(50),
col3 VARCHAR(50) NOT NULL,
constraint PK_test primary key (col1, col3) not enforced
);
MERGE INTO TEST AS tgt
USING (
SELECT COL1, COL2, COL3 FROM (
SELECT 'a' COL1, 'b' COL2, 'a' COL3
UNION
SELECT 'a' COL1, 'c' COL2, 'a' COL3
UNION
SELECT 'a' COL1, 'd' COL2, 'a' COL3)
QUALIFY row_number() over (partition by COL1, COL3 order by COL2) = 1
)AS src
ON (tgt.COL1=src.COL1 AND tgt.COL3=src.COL3)
WHEN NOT MATCHED
THEN INSERT (COL1, COL2, COL3)
VALUES (src.COL1, src.COL2, src.COL3);
Only the row a,b,a was inserted.

SQL Check constraint on column referencing other columns

I want to limit a column that it can only have a value when another column has a value.
example: (this doesn't work)
create table testConstraint (
col1 int not null identity(1, 1) primary key,
col2 int,
col3 int check (col2 is not null),
col4 int)
This is not possible because he cannot reference another column.
Error:
Column CHECK constraint for column 'col3' references another column,
table 'testConstraint'.
Another try was: (also doesn't work)
create table testConstraint (
col1 int not null identity(1, 1) primary key,
col2 int,
col3 int,
col4 int)
GO
alter table testConstraint add constraint ck_columnNotNull check (case when col2 is null then col3 is null end)
GO
Anyone have an idea how this would be possible with a constraint?
You can write a trigger.
Also, you can try this
(1)
ALTER TABLE TestConstraint ADD CONSTRAINT
CK_TestConstraint CHECK (NOT ( (col3 is not null) and (col2 is null) ))
GO
or this
(2)
ALTER TABLE TestConstraint ADD CONSTRAINT
CK_TestConstraint CHECK
(
((col3 is not null) and (col2 is not null)) or
((col3 is null) and (col2 is null))
)
GO
depending on what exactly you need.
I just tested it and it works OK, I think.
insert into
TestConstraint
(col2, col3, col4)
values
(null, 1, 2)
-- ERROR
insert into
TestConstraint
(col2, col3, col4)
values
(1, 1, 2)
-- OK
ALTER TABLE testConstraint
ADD CONSTRAINT ck_columnNotNull
CHECK ( 1 = CASE
WHEN col2 IS NULL AND col3 IS NULL THEN 1
WHEN col2 IS NOT NULL AND col3 IS NOT NULL THEN 1
ELSE 0
END)
Only simple logic is required, plus it needs (as per your second attempt) to be a table check constraint, so you can't declare it inline with the declaration of col3:
create table testConstraint (
col1 int not null identity(1, 1) primary key,
col2 int,
col3 int,
col4 int)
GO
alter table testConstraint add constraint ck_columnNotNull check (
col3 is null
or col2 is not null
)
GO
If col3 is null, then we don't care what the value of col2 is. Conversely, if it's not NULL, then we do want to enforce the col2 isn't null. That's what the two sides of the or effectively give us.

SQL SERVER - Using DATEDIFF with subquery column

I have a query that looks like:
SELECT
col1
,...
,col3
,(SELECT col3 FROM table where <clause>) AS MinPickTime
,(SELECT col3 FROM table where <clause>) AS MaxPickTime
,DATEDIFF(d, MinPickTime, MaxPickTime)
FROM table
However the DATEDIFF line does not like the alias columns.
In short, how do I give DATEDIFF an alias column derived by a subquery?
Use derived tables concept to access alias name.
SELECT col1
,...
,col3,
MinPickTime,
MaxPickTime ,
DATEDIFF(d, MinPickTime, MaxPickTime)
FROM (
SELECT
col1
,...
,col3
,(SELECT col3 FROM table where <clause>) AS MinPickTime
,(SELECT col3 FROM table where <clause>) AS MaxPickTime
FROM table
)z
with temp as (SELECT
col1
,...
,col3
,(SELECT col3 FROM table where <clause>) AS MinPickTime
,(SELECT col3 FROM table where <clause>) AS MaxPickTime
FROM table)
select DATEDIFF(d,MaxPickTime,MinPickTime) from temp

Dependent insert statements

I have a table with data about a customer, Customer(name, address), with rows like "John Doe", "Some Street 123". For each row in the table, I want to insert one row in the Person(id, name) table and also one row in the Address(id, person_id, address) table.
I can accomplish this by running two insert statements for each row in Customer:
insert into Person(name) values (#name);
insert into Address(person_id, address) values (scope_identity(), #address);
But this is inefficient. I want to do the inserts in a batch, kind of like this:
-- This works, the problem is with the Address table...
insert into Person(name)
select name from Customer
-- This looks good but does not work because name is not unique.
insert into Address(person_id, address)
select p.person_id, c.address
from Customer c join Person p on c.name = p.name
Leaving this here for the fellow Google traveler that finds this post like me.
I found this solution, and it seems to work great, and doesn't require any funky schema alterations:
https://dba.stackexchange.com/questions/160210/splitting-data-into-two-tables-in-one-go
They use a MERGE statement to perform the initial insert into the first table (the table that is generating the identity to be used everywhere else). The reason it uses the MERGE statement is because it allows you to use an OUTPUT statement, which you can use to output both the new identity value as well as the identity value from the source table (as opposed to using an OUTPUT statement on a standard INSERT which does not allow you to output the source tables identity). You can insert this output data into a mapping table, and use that mapping table to perform the second insert.
Here's my sample code for the solution:
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- Set up sample schema and data
------------------------------------------------------------------------------
--Source Data
IF OBJECT_ID('dbo.tmp1') IS NOT NULL DROP TABLE dbo.tmp1 --SELECT * FROM dbo.tmp1
CREATE TABLE dbo.tmp1 (tmp1ID INT IDENTITY(1,1), Col1 CHAR(1) NOT NULL, Col2 CHAR(1) NOT NULL, Col3 CHAR(1) NOT NULL, Col4 CHAR(1) NOT NULL, Col5 CHAR(1) NOT NULL, Col6 CHAR(1) NOT NULL)
INSERT INTO dbo.tmp1 (Col1, Col2, Col3, Col4, Col5, Col6)
SELECT x.c1, x.c2, x.c3, x.c4, x.c5, x.c6
FROM (VALUES ('A','B','C','D','E','F'),
('G','H','I','J','K','L'),
('M','N','O','P','Q','R')
) x(c1,c2,c3,c4,c5,c6)
IF OBJECT_ID('dbo.tmp3') IS NOT NULL DROP TABLE dbo.tmp3 --SELECT * FROM dbo.tmp3
IF OBJECT_ID('dbo.tmp2') IS NOT NULL DROP TABLE dbo.tmp2 --SELECT * FROM dbo.tmp2
--Taget tables to split into
CREATE TABLE dbo.tmp2 (
tmp2ID INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_tmp2 PRIMARY KEY CLUSTERED (tmp2ID ASC)
, Col1 CHAR(1) NOT NULL
, Col2 CHAR(1) NOT NULL
, Col3 CHAR(1) NOT NULL
)
CREATE TABLE dbo.tmp3 (
tmp2ID INT NOT NULL
, Col4 CHAR(1) NOT NULL
, Col5 CHAR(1) NOT NULL
, Col6 CHAR(1) NOT NULL
, CONSTRAINT FK_tmp3_tmp2ID FOREIGN KEY(tmp2ID) REFERENCES dbo.tmp2 (tmp2ID)
)
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- Split data into two tables
------------------------------------------------------------------------------
DECLARE #Mapping TABLE (tmp1ID INT NOT NULL, tmp2ID INT NOT NULL);
--Use merge statment to output the source data PK as well as the newly inserted identity to generate a mapping table
MERGE INTO dbo.tmp2 AS tgt
USING dbo.tmp1 AS src ON (1=0)
WHEN NOT MATCHED THEN
INSERT ( Col1, Col2, Col3)
VALUES (src.Col1, src.Col2, src.Col3)
OUTPUT src.tmp1ID, Inserted.tmp2ID INTO #Mapping (tmp1ID, tmp2ID);
--Use the mapping table to insert the split data into the second table
INSERT INTO dbo.tmp3 (tmp2ID, Col4, Col5, Col6)
SELECT t2.tmp2ID, t1.Col4, t1.Col5, t1.Col6
FROM dbo.tmp2 t2
JOIN #Mapping m ON m.tmp2ID = t2.tmp2ID
JOIN dbo.tmp1 t1 ON t1.tmp1ID = m.tmp1ID
SELECT tmp2ID, Col1, Col2, Col3 FROM dbo.tmp2
SELECT tmp2ID, Col4, Col5, Col6 FROM dbo.tmp3
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- Clean up
------------------------------------------------------------------------------
DROP TABLE dbo.tmp1
DROP TABLE dbo.tmp3
DROP TABLE dbo.tmp2
------------------------------------------------------------------------------
------------------------------------------------------------------------------
GO
there is no way to do this as you explain because you lost scope_identity() value of each row of first insert.
A work around may be add Customer primary key fields to Person table and then make join of second insert with this fields:
before insert create customerID field on Person
alter table Person add customerID int null;
then bulk inserts:
-- inserting customerID
insert into Person(name, customerID)
select name, customerID from Customer
-- joining on customerID.
insert into Address(person_id, address)
select p.person_id, c.address
from Customer c
join Person p on c.customerID = p.customerID
after that you can remove customerID field from Person table:
alter table Person drop column customerID
It's better that you create some field of unique types in both table are related them.otherwise you want join as you dont have unique field for condition