How to Declaring scalar table in sql? - sql-server

How to declare one element table so it can be used in future queries?
DECLARE #Emp TABLE
(
ID BIGINT NULL,
CompanyID BIGINT NULL
)
INSERT INTO #EMP
SELECT ID,CompanyID FROM Emp WHERE PIN = 123
SELECT COMPANYID FROM COMPANY WHERE ID = #Emp.CompanyID

You can not. A table per definition contains a (possibly) unlimited number of elements. However, you can always do something like this:
DECLARE #CompanyID BIGINT
SET #CompanyID = (SELECT TOP 1 CompanyID FROM #Emp WHERE ...)
By the way, the following line is not correct, as the WHERE clause is incomplete.
SELECT COMPANYID FROM COMPANY WHERE #Emp.CompanyID

If you intention is to create a table variable that will only store a maximum of one row, you can do it like this:
DECLARE #Emp TABLE
(
ID BIGINT NULL,
CompanyID BIGINT NULL,
Lock char(1) not null default 'X' primary key check(Lock='X')
)
INSERT INTO #EMP (ID,CompanyID)
SELECT ID,CompanyID FROM Emp WHERE PIN = 123
Because the table's primary key is constrained to only one possible value, logically there can't be more than one row.

Related

UPDATE with result from INSERT query

I have an existing table, A, defined as follows:
CREATE TABLE dbo.A(
id int IDENTITY(1, 1) NOT NULL PRIMARY KEY CLUSTERED,
name nvarchar(255) NOT NULL UNIQUE NONCLUSTERED
);
This table has 1,000s of existing records.
I now require a new table, B, defined as follows:
CREATE TABLE dbo.B(
id int IDENTITY(1, 1) NOT NULL PRIMARY KEY CLUSTERED,
some_value int NOT NULL
);
I need to create a record in B for each record in A and add a reference from the A record to the corresponding B record. Firstly, I need to add a new field to table A that references table B (which necessarily must be nullable with a default value of NULL, at this stage):
ALTER TABLE dbo.A
ADD b_id int NULL references dbo.B;
How can I create the necessary B records and update the A.b_id field accordingly? I'm looking for something like this:
UPDATE dbo.A
SET b_id = (INSERT INTO dbo.B(some_value) VALUES(5));
such that the value of b_id is the id field of the newly inserted B. (Clearly, this query isn't valid.)
The only solution I can come up with is to write a complex stored procedure using multiple, separate queries. Is there a way to do this with a single query?
IF I am understanding correctly, there's no need for a CURSOR here. What you could do is a MERGE that allows your to OUTPUT both columns from the source data and the target:
CREATE TABLE dbo.A(
id int IDENTITY(1, 1) NOT NULL PRIMARY KEY CLUSTERED,
name nvarchar(255) NOT NULL UNIQUE NONCLUSTERED
);
GO
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N))
INSERT INTO dbo.A
SELECT NEWID()
FROM N N1, N N2, N N3;
GO
CREATE TABLE dbo.B(
id int IDENTITY(1, 1) NOT NULL PRIMARY KEY CLUSTERED,
some_value int NOT NULL
);
GO
ALTER TABLE dbo.A
ADD b_id int NULL references dbo.B;
GO
DECLARE #IDs table (a_id int, b_id int)
MERGE dbo.B B
USING dbo.A A ON B.id = A.b_id
WHEN NOT MATCHED BY TARGET THEN
INSERT (some_value)
VALUES(RAND() * 1000)
OUTPUT A.id, inserted.id
INTO #IDs;
UPDATE A
SET b_id = I.b_id
FROM dbo.A A
JOIN #Ids I ON A.id = I.a_id;
GO
SELECT *
FROM dbo.A;
GO
DROP TABLE dbo.A;
DROP TABLE dbo.B;
db<>fiddle
If you are willing (and able) to make a slight adjustment to TableB as well, you can use OUTPUT when inserting. I added a_id to TableB for my example. Updating b_id back to TableA is a bit redundant as you would now have TableA's ID in TableB for joining.
Here's an example.
-- Mock tables.
DECLARE #TableA table ( id int IDENTITY (100,1) PRIMARY KEY, [name] nvarchar(255), b_id int );
DECLARE #TableB table ( id int IDENTITY (1,1) PRIMARY KEY, a_id int, some_value int );
-- Mock data.
INSERT INTO #TableA ( [name] ) VALUES
( 'TableA_1' ), ( 'TableA_2' ), ( 'TableA_3' );
-- Create an OUTPUT table to capture the values inserted into TableB.
DECLARE #out table ( a_id int, b_id int );
INSERT INTO #TableB ( a_id, some_value )
OUTPUT inserted.a_id, inserted.id INTO #out
SELECT
id AS a_id, 999 AS some_value
FROM #TableA;
-- Update TableA's b_id column.
UPDATE #TableA
SET
b_id = o.b_id
FROM #TableA AS a
INNER JOIN #out AS o
ON a.id = o.a_id;
-- Show updated results.
SELECT * FROM #TableA ORDER BY id;
Returns
+-----+----------+------+
| id | name | b_id |
+-----+----------+------+
| 100 | TableA_1 | 1 |
| 101 | TableA_2 | 2 |
| 102 | TableA_3 | 3 |
+-----+----------+------+

Auto increment column each time 1,2,3 SQL server

I have temp table each time store 100 values based on a specific condition.
I need Slno as 1,2,3,4 ...100 each time query executes .
If I use below syntax's, the 'Slno' is taking some other numbers
create table #temptable
(Slno INT IDENTITY(1,1) NOT NULL ,
Name varchar(50)
)
create table #temptable
(Slno int IDENTITY(1,1) PRIMARY KEY ,
Name varchar(50)
)
Please help if there is a way out without using Rank()?
You need to create an IDENTITY column as follows:
Syntax:
CREATE TABLE (
ID_column INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
...
);
It should be
Identity(seed,increment)
Here you go:
CREATE TABLE #temptable
(Slno INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
Name varchar(50)
)
Example:
INSERT INTO #temptable (Name) Values ('ABC')
INSERT INTO #temptable (Name) Values ('ABhshC')
INSERT INTO #temptable (Name) Values ('ABQRAC')
INSERT INTO #temptable (Name) Values ('ABhsAERAYRHAERhC')
SELECT * FROM #temptable
Results:
Slno Name
1 ABC
2 ABhshC
3 ABQRAC
4 ABhsAERAYRHAERhC

How can I insert a newly generated IDENTITY value into a related table with other fields?

I have a data table that contains a name and a social security number. I want to insert the name into a table with an identity field, then insert the ssn with that new identity field value into another table.
Below are the tables:
CREATE TABLE [data_table]
(
[name] [varchar](50) NOT NULL,
[ssn] [varchar](9) NOT NULL,
)
CREATE TABLE [entity_key_table]
(
[entity_key] [int] IDENTITY(1000000,1) NOT NULL,
[name] [varchar](50) NOT NULL,
)
CREATE TABLE [entity_identifier_table]
(
[entity_identifier_key] [int] IDENTITY(1000000,1) NOT NULL,
[entity_key] [int] NOT NULL,
[ssn] [int] NOT NULL,
)
This query works but doesn't link entity_key in [entity_key_table] TO ssn in [entity_identifier_table]:
INSERT INTO entity_key_table (name)
OUTPUT [INSERTED].[entity_key]
INTO [entity_identifier_table] (entity_key)
SELECT [name]
FROM [data_table]
This is what I want to do, but it doesn't work.
INSERT INTO entity (name)
OUTPUT [INSERTED].[entity_key], [data_table].[ssn]
INTO [entity_identifier] (entity_key,ssn)
SELECT [name]
FROM [data_table]
Rewriting my answer based on your requirements and the articles you linked. I think you can get that behavior doing something like this. I admit, I have never seen a merge on something like 1 != 1 like the article suggests, so I would be very cautious with this and test the bajeezes out out of it.
FWIW, it looks like during an INSERT, you can't access data that's not in the inserted virtual table, but updates (and apparently MERGE statements) can.
if object_id('tempdb.dbo.#data_table') is not null drop table #data_table
create table #data_table
(
[name] [varchar](50) NOT NULL,
[ssn] [varchar](9) NOT NULL,
)
if object_id('tempdb.dbo.#entity_key_table') is not null drop table #entity_key_table
create table #entity_key_table
(
[entity_key] [int] IDENTITY(1000000,1) NOT NULL,
name varchar(50)
)
if object_id('tempdb.dbo.#entity_identifier_table') is not null drop table #entity_identifier_table
create table #entity_identifier_table
(
[entity_identifier_key] [int] IDENTITY(2000000,1) NOT NULL,
[entity_key] [int] NOT NULL,
[ssn] varchar(9) NOT NULL,
)
insert into #Data_table (Name, SSN)
select 'John', '123456789' union all
select 'John', '001100110' union all
select 'Jill', '987654321'
merge into #entity_key_table t
using #data_table s
on 1 != 1
when not matched then insert
(
name
)
values
(
s.name
)
output inserted.entity_key, s.ssn
into #entity_identifier_table
(
entity_key,
ssn
);
select top 1000 *
from #data_table
select top 1000 *
from #entity_key_table
select top 1000 *
from #entity_identifier_table
The problem with your code is that you output data only from inserted or deleted.
Assuming your name column only relates to one SSN, the following would work:
DECLARE #output TABLE (entity_key INT,ssn VARCHAR (11))
INSERT INTO entity (entity_key, name)
OUTPUT [INSERTED].[entity_key], [inserted].[name]
INTO #output
SELECT D.Entity_key, d.name
FROM datatable
INSERT INTO entity_identifier (entity_key, ssn)
Select o.entity_key, d.snn
from #output o
join datatable d on o.name = d.name
However, the problem of multiple duplicated names having different Social Security Numbers is extremely high. In this case, your current structure simply does not work because there is no way to know which identity belongs to which name record. (The Merge solution in another post may also have this problem, before you put that to production be sure to test the scenario of duplicated names. The chances of duplicated names in a set of records is extremely high in any reasonable large data set of names and this should be one of your unit test for any potential solution.)
Here is a potential workaround. First, insert the SSN as the name in the first insert, then return output as shown but join on the #output Name column to the SSN column. After doing the other insert, then update the name in the orginal table to the correct name again joining on the SSN data.
DECLARE #output TABLE (entity_key INT,ssn VARCHAR (11))
INSERT INTO entity (entity_key, name)
OUTPUT [INSERTED].[entity_key], [inserted].[ssn]
INTO #output
SELECT D.Entity_key, d.name
FROM datatable
INSERT INTO entity_identifier (entity_key, ssn)
Select o.entity_key, d.output
from #output o
update e
set name = d.name
FROM entity e
join #output o on e.entity_key = o.entity_key
join datatable d on o.name = d.ssn

creating before INSERT TRIGGER comparing values between two tables

I need help creating a before insert trigger, as i am new to TSQL. below are the two tables.
SALARY table:
CREATE TABLE SALARY
(
StarName varchar(30) NOT NULL,
MovieTitle varchar(30)NOT NULL,
MovieYearMade numeric(4, 0) NOT NULL,
Amount numeric(8, 0) NULL,
PRIMARY KEY (MovieTitle,StarName,MovieYearMade),
)
MOVIESTAR table
CREATE TABLE MOVIESTAR
(
Name varchar(30) NOT NULL,
Address varchar(20),
City varchar(15) DEFAULT ('Palm Springs'),
Gender char(1) NULL CHECK (Gender ='M' OR GENDER ='F'),
BirthYear Numeric(4),
PRIMARY KEY CLUSTERED (Name)
)
I want to create a trigger so when a new movie is added. It prevents adding SALARY.Amount if SALARY.MovieYearMade is before MOVIESTAR.BirthYear.
I am confused as how to define trigger, when I am comparing values in two tables i.e. SALARY and MOVIESTAR.
thanks,
Are you looking for something like this?
CREATE TRIGGER tg_salary ON salary
INSTEAD OF INSERT AS
BEGIN
INSERT INTO salary (StarName, MovieTitle, MovieYearMade, Amount)
SELECT i.StarName, i.MovieTitle, i.MovieYearMade,
CASE WHEN i.MovieYearMade < s.BirthYear THEN NULL ELSE i.Amount END
FROM INSERTED i JOIN moviestar s
ON i.StarName = s.Name
END
Here is SQLFiddle demo

Complex multi-column unique constraint

I have a table with 10 columns but only care about 3 columns for this. Imagine my table looks like this:
CREATE TABLE MyTable ( RowID int IDENTITY(1,1), UserID int, NodeID int, RoleID int )
What I need is a constraint that enforces the following: UserID and RoleID need to be unique for each NodeID (i.e. a user cannot have the same role in multiple nodes). In other words I want to allow
INSERT MyTable (UserID, NodeID, RoleID) SELECT 1, 1, 1
but not allow
INSERT MyTable (UserID, NodeID, RoleID) SELECT 1, 2, 1
if the first insert has occurred because that would result in a user having a role in multiple nodes.
Hopefully this is simple and I'm just making it more complex than it needs to be in my brain.
Since your constraint depends on data in other rows, this rules out a filtered index. IMO a viable option could be a trigger. Such a trigger could look like something like this:
CREATE TRIGGER dbo.MyTrigger ON dbo.Q1
AFTER INSERT, UPDATE
AS
DECLARE #userId INT, #Id INT, #roleId INT, #exists INT;
SELECT TOP 1
#userId = userID
,#roleId = roleID
,#Id = Id
FROM inserted;
SELECT TOP 1
#exists = Id
FROM Q1
WHERE userId = #userId
AND roleID = #roleID AND Id<> #Id;
IF ISNULL(#exists, 0) > 0
BEGIN
-- you would want to either undo the action here when you use an 'after' trigger
-- because as the name implies ... the after means the record is allready inserted/updated
RAISERROR ('No way we would allow this.', 16, 1);
END
-- else
-- begin
-- another alternative would be to use a instead of trigger, which means the record
-- has not been inserted or updated and since that type of trigger runs the trigger 'instead of'
-- updating or inserting the record you would need to do that yourself. Pick your poison ...
-- end
GO
An unique index should enforce your requirements
CREATE UNIQUE NONCLUSTERED INDEX [idx_Unique] ON [dbo].[MyTable]
(
[UserID] ASC,
[NodeID] ASC,
[RoleID] ASC
)
From the comments I suppose you will need two unique indices
CREATE UNIQUE NONCLUSTERED INDEX [idx_User_Node] ON [dbo].[MyTable]
(
[UserID] ASC,
[NodeID] ASC
)
GO
CREATE UNIQUE NONCLUSTERED INDEX [idx_User_Role] ON [dbo].[MyTable]
(
[UserID] ASC,
[RoleID] ASC
)

Resources