How do i insert new record in this table? - sql

I'm sorry for this noob question.
There're 2 tables: the smaller one is derived from bigger one.
How do i insert new record into bigger table??
INSERT INTO people (lname, fname, city, age, salary) VALUES (' Doe','John','Paris', '25','1000$' );
but bigger table contains city as number. How should i insert 'Paris'?? Should i know its number beforehand?? But 'Paris' isn't in Cities (smaller) table!!
How do records get inserted in bigger (people) table??

EDIT:
Added IF block to check for Paris.
IF NOT EXISTS (SELECT 1 FROM City WHERE City = 'Paris')
Insert INTO City (City) VALUES ('Paris')
DECLARE #Cid int = (SELECT CityID FROM City WHERE City = 'Paris')
INSERT INTO people (lname, fname, city, age, salary)
VALUES (' Doe','John', #cid, '25','1000$' )
I made an assumption about the structure of the city table obviously.
You could also parameterize this with a #city variable and sub that for 'Paris' everywhere in the code.

In the insert to the people table, the value of the city column should be the number of the city you want the person to be linked to - so in your quoted example replace 'Paris' with the city number for Paris.
If you want to create a new person record with a city that does not yet exist in the cities table, you'll need to do an insert into the cities table first, get the number of the created city and then use that in your insert to the people table.

EDIT:
adopted solution for existing entries in city table. as I wrote in comments, transactions could be ommited (for example when running as stored procedure)
BEGIN TRANSACTION
DECLARE #city_id INT
SELECT #city_id=id FROM city WHERE name='Paris'
IF #city_id IS NULL
BEGIN
INSERT INTO city (name) VALUES ('Paris')
SET #city_id=##IDENTITY
END
INSERT INTO people (lname, fname, city, age, salary) VALUES ('Doe','John',#city_id, '25','1000$' );
END TRANSACTION

Related

Get unique row based on 2 columns with duplicate values

I have a table with 3 columns and 6 rows:
As you can see based on the highlighted red text, Ash and Joey have the same Last name and Street address i.e. column "Last" and column "Street" have a duplicate value. I would like to only get one of them.
Desired result would be to get rows without duplicate values on the "Last" and "Street" columns:
Where only one of Ash or Joey is retained (I just used Ash in this example, but Joey would be fine too - just need 1 or the other, not both).
Is this even possible? Any advise appreciated, thanks.
P.S. the “Street” column is actually on a different table so the picture of the graph represents 2 tables already joined.
Since you don't care which record of the duplicates survives you can give this a shot. It'll actually keep the first one alphabetically by First
DROP TABLE IF EXISTS #t;
CREATE TABLE #t (First VARCHAR(255), Last VARCHAR(255), Street VARCHAR(255));
INSERT #t SELECT 'Ash', 'Williams', '123 Main';
INSERT #t SELECT 'Ben', 'O''Shea', '456 Grand';
INSERT #t SELECT 'Claire', 'Port', '543 Jasper';
INSERT #t SELECT 'Denise', 'Stone', '543 Jasper';
INSERT #t SELECT 'Erica', 'Thomas', '789 Holt';
INSERT #t SELECT 'Joey', 'Williams', '123 Main';
WITH dupes AS (
SELECT First,
Last,
Street,
ROW_NUMBER() OVER (PARTITION BY Last, Street ORDER BY First) RowNum
FROM #t
)
SELECT First, Last, Street
FROM dupes
WHERE RowNum = 1;
On the assumption you want to retain the person with the first name alpabetically, you can use the ROW_NUMBER window function to generate a new row number for each duplicate and use that to filter out the dupes:
CREATE TABLE Peeps
(
FirstName NVARCHAR(20),
LastName NVARCHAR(20),
Street NVARCHAR(20)
)
INSERT INTO Peeps
VALUES
('Ash','Williams','123 Main'),
('Ben','O''Shea','456 grand'),
('Claire','Port','543 Jasper'),
('Denise','Stone','543 Jasper'),
('Erica','Thomas','789 Holt'),
('Joey','Williams','123 Main')
SELECT FirstName,
LastName,
Street
FROM (
SELECT FirstName,
LastName,
Street,
ROW_NUMBER () OVER (PARTITION BY LastName,Street ORDER BY FirstName) AS RowN
FROM Peeps
) a
WHERE RowN = 1
DROP TABLE Peeps

Is there a way to have a conditional "IN" statement in the WHERE clause of a procedure?

Let's say I have a table called "Student" with the following columns: FirstName, LastName, Grade. Now let's say I have a procedure called "getStudents" where you can pass a list of comma delimited grades to select from. It looks something like this:
CREATE PROCEDURE [dbo].[getStudents] (
#GradeList VARCHAR(MAX)
)
AS
BEGIN
--Declare temporary table for grades
CREATE TABLE #Grades (Grade varchar(50))
--Insert the grades from #GradeList parameter into the temp table
INSERT INTO
#Grades
SELECT value AS Grade
FROM
--This is a function that will split the comma delimited list and return the values
[dbo].[SplitValues] (#GradeList)
--Select all students that are in the grades provided
SELECT
FirstName,
LastName
FROM
Student
WHERE
Grade IN (SELECT Grade FROM #Grades)
END
This is all working as intended. But what we want now is to make #GradeList an optional parameter, if the value of it is NULL then we want to return all of the students. In this simplified example I would just do an IF statement but we actually want to have multiple optional parameters so it has to be part of the same SELECT statement. I was able to get this working with the following logic:
CREATE PROCEDURE [dbo].[getStudents] (
#GradeList VARCHAR(MAX) = NULL
)
AS
BEGIN
--Declare temporary table for grades
CREATE TABLE #Grades (Grade varchar(50))
--Insert list of grades into a temporary table
IF #GradeList IS NULL
BEGIN
--No grades provided, select all possible values for Grade into temp table
INSERT INTO
#Grades
SELECT DISTINCT
Grade
FROM
Student
END
ELSE
BEGIN
--Insert the grades from #GradeList parameter into the temp table
INSERT INTO
#Grades
SELECT value AS Grade
FROM
--This is a function that will split the comma delimited list and return the values
[dbo].[SplitValues] (#GradeList)
END
--Select all students that are in the grades provided
SELECT
FirstName,
LastName
FROM
Student
WHERE
Grade IN (SELECT Grade FROM #Grades)
END
So if nothing is passed in, I just add all of the possible values for Grade to the #Grades temporary table. Functionally this works and is doing what we want it to do. However, because we're dealing with large tables here this has added a tremendous amount of overhead to the procedure (now it's taking up to a minute to run, before it was ~1-2 seconds). Is there a way to have the condition in the WHERE clause be dependent on whether or not there is a value for #GradeList?
I think you want:
SELECT s.FirstName, s.LastName
FROM Student s
WHERE s.Grade IN (SELECT g.Grade FROM #Grades g) OR
#GradeList IS NULL;
There is no need to use a temporary table to store the results. You can just use split_string() directly in the query. If you do use a temporary table, though, you might as well make best use of it. Define the Grade to be the primary key:
CREATE TABLE #Grades (
Grade varchar(50) PRIMARY KEY
);
This provides more opportunities to optimize the later query.

SQL 2 Inserts based on return of first

I am generating a series of Inserts based on data from an Excel file into SQL Server 2014
How do I get the value of the ID of the first INSERT to put into the second
Simplified example where Ontario is a Province of Canada:
Insert into country (Name) values('canada');
Insert into provinces (CountryId, Name) values (???,'ontario');
There are 100 inserts so performance is not an issue.
declare #countryid int
Insert into country (Name) values('canada');
SELECT #countryid = SCOPE_IDENTITY()
Insert into provinces (CountryId, Name) values (#countryid,'ontario');
the answer above from tshoemake shows how you can insert one record and get the result. If you want to insert many records in Country and then many records in provinces, you might want to have a look at the OUTPUT clause. You'll have to work out how to join in your list of provinces because this code will just add Ontario to every country:
create table __country
(
id int identity(1,1) primary key,
Name varchar(5000)
)
CREATE TABLE __Provinces (
countryid int,
name varchar(5000)
)
CREATE TABLE #tempIDs
(
id int
)
INSERT INTO __Country
OUTPUT inserted.id
INTO #tempIDs
values ('canada'), values('USA')
insert into __Provinces
select #tempIDs.id, 'ontario'
from #tempIDs
join __country
ON __country.id = #tempIDs.id
select * from __Provinces

Split One table into Two in SQL Server 2008

I need to break one table (structure built by someone else but I need the data it contains thousands of records) into two new tables I created.
Table Name: Customers_Info (Old Table)
FullName Telephone Address
Adam Johnson 01555777 Michigan
John Smith 01222333 New York
John Smith 01222333 New Jersey
Lara Thomas 01888999 New Mexico
The above is the old table. Now I created two tables to hold the data, one table for customers with a default address, and the other table to hold additional addresses. In the shown example I need 3 persons to be listed in the Customers table, and the address of "John Smith" (the second one New Jersey) to be listed in the Addresses table.
The common field to look at here is "Telephone" and it's unique for every customer.
Here's how the result should be display.
Table Name: Customers (New Table)
CustomerID FullName Telephone Default_Address
1 Adam Johnson 01555777 Michigan
2 John Smith 01222333 New York
3 Lara Thomas 01888999 New Mexico
Table Name: Addresses (New Table)
AddressID CustomerID Address
1 2 New Jersey
Of course it was easy to copy all data into the new Customers table, but what I'm stuck at now, is how to remove the duplicates from Customers and insert them into the Addresses table with the Customer ID and Address only.
Thanks!
Give a try with below code and let me know the comments/results.
CREATE TABLE [Customers_Info]
(
FullName VARCHAR(50)
,Telephone VARCHAR(50)
,Address VARCHAR(50)
)
GO
CREATE TABLE Customers
(
CustomerID INT IDENTITY(1,1)
,FullName VARCHAR(50)
,Telephone VARCHAR(50)
,Default_Address VARCHAR(50)
)
GO
ALTER TABLE dbo.Customers ADD CONSTRAINT PK_Customers
PRIMARY KEY CLUSTERED (CustomerID);
GO
CREATE TABLE Addresses
(
AddressID INT IDENTITY(1,1)
,CustomerID INT
,[Address] VARCHAR(50)
)
GO
ALTER TABLE dbo.Addresses ADD CONSTRAINT PK_Addresses
PRIMARY KEY CLUSTERED (AddressID);
GO
ALTER TABLE Addresses ADD CONSTRAINT FK_CustomerID_Addresses_Customers FOREIGN KEY (CustomerID)
REFERENCES dbo.Customers(CustomerID);
GO
INSERT INTO [Customers_Info] VALUES ('Adam Johnson', '01555777', 'Michigan')
INSERT INTO [Customers_Info] VALUES ('John Smith' , '01222333', 'New York')
INSERT INTO [Customers_Info] VALUES ('John Smith' , '01222333', 'New Jersey')
INSERT INTO [Customers_Info] VALUES ('Lara Thomas' , '01888999', 'New Mexico')
INSERT INTO [Customers_Info] VALUES ('Lara Thomas' , '01888999', 'New Mexico1')
INSERT INTO [Customers_Info] VALUES ('Lara Thomas' , '01888999', 'New Mexico2')
INSERT INTO [Customers_Info] VALUES ('Adam Johnson', '01555777', 'Michigan1')
INSERT INTO [Customers_Info] VALUES ('Adam Johnson', '01555777A', 'Michigan')
INSERT INTO [Customers_Info] VALUES ('Adam Johnson', '01555777A', 'Michigan2')
GO
SELECT * FROM [Customers_Info]
--DELETE FROM Customers
--TRUNCATE TABLE Addresses
------------------------------------------------------------------------------------------------------------------
;WITH a as
(
SELECT FullName,Telephone,[Address],
rn = row_number() over (partition by FullName, Telephone order by FullName)
FROM [Customers_Info]
)
INSERT INTO Customers SELECT
FullName,Telephone,[Address] from a where rn = 1
------------------------------------------------------------------------------------------------------------------
;WITH b as
(
SELECT FullName,Telephone,[Address],
rn = row_number() over (partition by FullName, Telephone order by FullName)
FROM [Customers_Info]
)
INSERT INTO Addresses SELECT CI.CustomerID,b.[Address] FROM Customers CI
INNER JOIN b ON b.FullName=CI.FullName AND b.Telephone=CI.Telephone
WHERE b.rn>1
SELECT * FROM Customers
SELECT * FROM Addresses
DROP TABLE [Customers_Info]
GO
DROP TABLE Addresses
GO
DROP TABLE Customers
GO
It would be more normalized if you broke it up into one more table for three total tables. Have the Customers table that has only customer data, have the Address table (which you could possibly rename to State) that has only the Address, then a CustomerAddress table that has both keys to each of those tables as Foreign Keys.
I will start you off to begin:
INSERT INTO Customers (FullName, Telephone)
SELECT DISTINCT FullName, Telephone
FROM Customers_Info
You would do the same for Address. For the 3rd table, you would perform the lookups like this:
INSERT INTO CustomerAddress (CustomerID, AddressID)
SELECT C.CustomerID, A.AddressID
FROM Customers_Info CI
INNER JOIN Customers C
ON CI.Telephone = C.Telephone
INNER JOIN Address A
ON CI.Address = A.Address

SQL: Insert data from another table in a table that contains foreign keys

In a SQL DATABASE
I have a Table Users
Id Name Age AddressId
----+------+------+-----------
Where AddressId is a foreign key to a table names Addresses
The Addresses Table:
Id Country State City ZipCode
----+---------+------+------+---------
This is a ONE-TO-ONE Relationship: Each User Has 1 address and each address has one user
I have a new table named NEWUsers
Id Name
----+------
It has only Id and Name.
What i want to do is this:
Write a script to insert all the records From the NEWUSers Table into the Users Table.
I want The Age to be default 20 for all new users
And for each new user inserted I need to create a new Address record for him
the new Address record will have all it's values (country, city, state, zipcode) equal to "abcd" except the Id which will be used to set the foreign key AddressId for the new user)
How can I do that?
I tried the following:
INSERT INTO Users(Name, Age)
Values((SELECT Name FROM NewUsers),20)
But I don't know how to create a new Address record for each user inserted and specify the foreign key accordingly.
Thanks a lot for any help
One method would be with two queries, formed like the following:
INSERT INTO Addresses (Country, State, City, ZipCode)
SELECT 'abcd', 'abcd', 'abcd', 'abcd' FROM NewUsers
INSERT INTO Users (Name, Age, AddressId)
SELECT Name, 20, ?? FROM NewUsers
Edit: One simple way to link the users to the addresses would be to temporarily set the country to the username. Then you would have a link to determine the addressId. Once the users table is populated and linked up properly, you can set country back to the default value abcd:
insert addresses (country, state, city, zipcode)
select name, 'abcd', 'abcd', 'abcd' from newusers;
insert users (name, age, addressid)
select u.name, 20, a.id from newusers u
join addresses a on a.country = u.name;
update a
set a.country = 'abcd'
from addresses a join newusers u on a.country = u.name;
Demo: http://www.sqlfiddle.com/#!3/1f09b/8
There are more complex ways to do this if you want to guarantee transactional consistency if multiple inserts can happen simultaneously, or if you want to allow duplicate names, etc. But based on the example you've given and details so far, this method should work.
This is a little hacky, but does what you want in two statements - assuming no user is going to have the name 'abcd' or enter that for their country, and that you purge the NewUsers table after this operation:
INSERT dbo.Addresses(Country, State, City, ZipCode)
OUTPUT inserted.Country, 20, inserted.id
INTO dbo.Users
SELECT Name, 'abcd', 'abcd', 'abcd'
FROM dbo.NewUsers;
UPDATE a SET Country = 'abcd'
FROM dbo.Addresses AS a
INNER JOIN dbo.NewUsers AS nu
ON a.Country = nu.Name;
You Must Write Cursor For Insert In Users and Address Table With Forign key
DECLARE #AddressID INT,
#ID INT,
#Name NVARCHAR(50)
DECLARE UserCursor CURSOR FOR
SELECT ID, NAME
FROM NewUsers
OPEN UserCursor
FETCH NEXT FROM UserCursor INTO #ID, #Name
WHILE ##FETCH_STATUS =0 BEGIN
INSERT INTO Addresses(Country, State, City, ZipCode)
VALUES ('abcd', 'abcd', 'abcd', 'abcd')
SET #AddressID = SCOPE_IDENTITY()
INSERT INTO Users(Name, Age, AddressID)
VALUES (#Name, 20, #AddressID)
FETCH NEXT FROM UserCursor INTO #ID, #Name
END
CLOSE UserCursor
DEALLOCATE UserCursor
Assuming you can set the AddressId however you want...
INSERT Addresses
(
Id,
Country,
State,
City,
ZipCode
)
SELECT
Id,
'abcd',
'abcd',
'abcd',
'abcd'
FROM NEWUsers
INSERT Users
(
Id,
Name,
Age,
AddressId
)
SELECT
Id,
Name,
20,
Id
FROM NEWUsers
If AddressId is an identity column, you can disable the identity temporarily first. See SET IDENTITY_INSERT for info and examples.
Honestly, the answer is that you don't want to do what you think you want to do. If the users have one and only one address, as you stated, then you should have only one table (users) that contains the address fields. Modeling your data properly will make this problem go away intrinsically.