Create columns with a loop - c#

I have an MVC project and in the Database I want to create a table with one column and more columns in a loop to the count of a List defined somewhere in my solution. How to achieve this?
CREATE TABLE [dbo].[Table]
(
[Id] INT NOT NULL PRIMARY KEY,
[tag] NVARCHAR(MAX) NOT NULL
while... <something becomes MyList.Count>
[value] NVARCHAR(MAX)
)

I guess you have write a separate query for that looping after creation, which would be an ALTER TABLE.
Like,
WHILE(condition)
BEGIN
ALTER TABLE Table1 ADD column_name datatype
END
Here, the condition should be the count. The column name depends on how you pass the values. I would pass them as XML data in table format, then convert it to a table in SQL and then loop through them while altering.
Like,
WHILE((SELECT COUNT(id) FROM temp))
BEGIN
EXECUTE('ALTER TABLE Table1 ADD COLUMN '+(SELECT TOP 1 column_name FROM temp)+' NVARCHAR(MAX)')
DELETE FROM temp WHERE temp.id = (SELECT TOP 1 id FROM temp)
END
Hope this works :)

Related

Insert to SQL Server from table type fails if more than 1 row

The following procedure gets data from C# as a datatable. I am sure that the table is sent with multiple rows. If it does have multiple rows, then no data is inserted, but if the data table contains only one row, it inserts it without any problems.
ALTER PROCEDURE [dbo].[MergeContactInfo]
#ContactInfo dbo.[PersonContactTableType] READONLY
AS
BEGIN
MERGE INTO PersonContact AS pc
USING #ContactInfo AS ci ON pc.Person = ci.Person
WHEN MATCHED THEN
UPDATE SET pc.Value = ci.Value, pc.Type = ci.Type
WHEN NOT MATCHED THEN
INSERT VALUES(ci.Person, ci.Type, ci.Value);
END
Any solution why multiple rows not inserted and only one row is inserted, there's no unique constraints.
#Contact Info is the following
CREATE TYPE [dbo].[PersonContactTableType] AS TABLE(
[Person] [int] NOT NULL,
[Type] [tinyint] NOT NULL,
[Value] [varchar](100) NOT NULL,
PRIMARY KEY CLUSTERED ([Person] ASC) WITH (IGNORE_DUP_KEY = OFF)
)
GO
Check your C# datatable content. Each number in "Person" field must be unique. Procedure perfectly works as designed.
Your code works perfect on SQL side,
I tested playing with the content of the table variable and I see that it is merged with table data as expected
declare #CI as [PersonContactTableType];
insert into #CI values (11,1,'ERALPER'),(12,2,'The Phantom'),(13,2,'Lord Vader'),(14,1,'Kodyaz')
exec [MergeContactInfo] #CI;
go
select * from PersonContact

SQL Insert into Joined Tables [duplicate]

My database contains three tables called Object_Table, Data_Table and Link_Table. The link table just contains two columns, the identity of an object record and an identity of a data record.
I want to copy the data from DATA_TABLE where it is linked to one given object identity and insert corresponding records into Data_Table and Link_Table for a different given object identity.
I can do this by selecting into a table variable and the looping through doing two inserts for each iteration.
Is this the best way to do it?
Edit : I want to avoid a loop for two reason, the first is that I'm lazy and a loop/temp table requires more code, more code means more places to make a mistake and the second reason is a concern about performance.
I can copy all the data in one insert but how do get the link table to link to the new data records where each record has a new id?
In one statement: No.
In one transaction: Yes
BEGIN TRANSACTION
DECLARE #DataID int;
INSERT INTO DataTable (Column1 ...) VALUES (....);
SELECT #DataID = scope_identity();
INSERT INTO LinkTable VALUES (#ObjectID, #DataID);
COMMIT
The good news is that the above code is also guaranteed to be atomic, and can be sent to the server from a client application with one sql string in a single function call as if it were one statement. You could also apply a trigger to one table to get the effect of a single insert. However, it's ultimately still two statements and you probably don't want to run the trigger for every insert.
You still need two INSERT statements, but it sounds like you want to get the IDENTITY from the first insert and use it in the second, in which case, you might want to look into OUTPUT or OUTPUT INTO: http://msdn.microsoft.com/en-us/library/ms177564.aspx
The following sets up the situation I had, using table variables.
DECLARE #Object_Table TABLE
(
Id INT NOT NULL PRIMARY KEY
)
DECLARE #Link_Table TABLE
(
ObjectId INT NOT NULL,
DataId INT NOT NULL
)
DECLARE #Data_Table TABLE
(
Id INT NOT NULL Identity(1,1),
Data VARCHAR(50) NOT NULL
)
-- create two objects '1' and '2'
INSERT INTO #Object_Table (Id) VALUES (1)
INSERT INTO #Object_Table (Id) VALUES (2)
-- create some data
INSERT INTO #Data_Table (Data) VALUES ('Data One')
INSERT INTO #Data_Table (Data) VALUES ('Data Two')
-- link all data to first object
INSERT INTO #Link_Table (ObjectId, DataId)
SELECT Objects.Id, Data.Id
FROM #Object_Table AS Objects, #Data_Table AS Data
WHERE Objects.Id = 1
Thanks to another answer that pointed me towards the OUTPUT clause I can demonstrate a solution:
-- now I want to copy the data from from object 1 to object 2 without looping
INSERT INTO #Data_Table (Data)
OUTPUT 2, INSERTED.Id INTO #Link_Table (ObjectId, DataId)
SELECT Data.Data
FROM #Data_Table AS Data INNER JOIN #Link_Table AS Link ON Data.Id = Link.DataId
INNER JOIN #Object_Table AS Objects ON Link.ObjectId = Objects.Id
WHERE Objects.Id = 1
It turns out however that it is not that simple in real life because of the following error
the OUTPUT INTO clause cannot be on
either side of a (primary key, foreign
key) relationship
I can still OUTPUT INTO a temp table and then finish with normal insert. So I can avoid my loop but I cannot avoid the temp table.
I want to stress on using
SET XACT_ABORT ON;
for the MSSQL transaction with multiple sql statements.
See: https://msdn.microsoft.com/en-us/library/ms188792.aspx
They provide a very good example.
So, the final code should look like the following:
SET XACT_ABORT ON;
BEGIN TRANSACTION
DECLARE #DataID int;
INSERT INTO DataTable (Column1 ...) VALUES (....);
SELECT #DataID = scope_identity();
INSERT INTO LinkTable VALUES (#ObjectID, #DataID);
COMMIT
It sounds like the Link table captures the many:many relationship between the Object table and Data table.
My suggestion is to use a stored procedure to manage the transactions. When you want to insert to the Object or Data table perform your inserts, get the new IDs and insert them to the Link table.
This allows all of your logic to remain encapsulated in one easy to call sproc.
If you want the actions to be more or less atomic, I would make sure to wrap them in a transaction. That way you can be sure both happened or both didn't happen as needed.
You might create a View selecting the column names required by your insert statement, add an INSTEAD OF INSERT Trigger, and insert into this view.
Before being able to do a multitable insert in Oracle, you could use a trick involving an insert into a view that had an INSTEAD OF trigger defined on it to perform the inserts. Can this be done in SQL Server?
Insert can only operate on one table at a time. Multiple Inserts have to have multiple statements.
I don't know that you need to do the looping through a table variable - can't you just use a mass insert into one table, then the mass insert into the other?
By the way - I am guessing you mean copy the data from Object_Table; otherwise the question does not make sense.
//if you want to insert the same as first table
$qry = "INSERT INTO table (one, two, three) VALUES('$one','$two','$three')";
$result = #mysql_query($qry);
$qry2 = "INSERT INTO table2 (one,two, three) VVALUES('$one','$two','$three')";
$result = #mysql_query($qry2);
//or if you want to insert certain parts of table one
$qry = "INSERT INTO table (one, two, three) VALUES('$one','$two','$three')";
$result = #mysql_query($qry);
$qry2 = "INSERT INTO table2 (two) VALUES('$two')";
$result = #mysql_query($qry2);
//i know it looks too good to be right, but it works and you can keep adding query's just change the
"$qry"-number and number in #mysql_query($qry"")
I have 17 tables this has worked in.
-- ================================================
-- Template generated from Template Explorer using:
-- Create Procedure (New Menu).SQL
--
-- Use the Specify Values for Template Parameters
-- command (Ctrl-Shift-M) to fill in the parameter
-- values below.
--
-- This block of comments will not be included in
-- the definition of the procedure.
-- ================================================
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE InsetIntoTwoTable
(
#name nvarchar(50),
#Email nvarchar(50)
)
AS
BEGIN
SET NOCOUNT ON;
insert into dbo.info(name) values (#name)
insert into dbo.login(Email) values (#Email)
END
GO

Sql or linq to sql Unique Constraint based on two columns value

I don't know is it possible or not.
I have a table that keeps records for a book issue return.There are two columns in this one is [status] and other [bookid] .I want to add a constraint in sql that will restrict user to insert duplicate record for status="issue" with same bookid.
For example if there is already a record with status='issue' and bookid=1 then it must not allow to insert other record with status="issue" and bookid=1 but there can me multipule records with other status like staus='return' and bookid=1 may occur a number of times.
Or there may be solution using linq to sql in c#
You do not need a user defined function, in general. In SQL Server (and many other databases) you can just use a filtered index:
create unique index unq_bookissue
where status = 'issued' ;
In earlier versions of SQL Server you can do this with a computed column, assuming that you have a table with columns such as:
BookId, which is repeated across rows.
Status, which should be unique when the value is issue.
BookIssueId, which uniquely identifies each row.
Then, add a computed column to keep track of status = 'issue':
alter table t add computed_bookissueid as (case when status = 'issue' then -1 else BookIssueId end);
Now add a unique index on this column and BookId:
create unique index unq_bookid_issue on (BookId, computed_bookissueid);
You have a complex condition here, so a UNIQUEconstraint won't help you. You will need a CHECKconstraint.
You first need a function to to do your check:
CREATE FUNCTION dbo.IsReturnDuplicate
(
#id INT,
#bookid INT,
#status VARCHAR(MAX)
)
RETURNS BIT
AS
BEGIN
RETURN (SELECT TOP 1 COUNT (*) FROM bookreturns WHERE (id <> #id) AND (status = #status) AND (bookid = #bookid) AND (status = 'issue')
END
This will return 1 if there is already a row in the table that has status 'issue' and has a different id
You can then create a CHECK constraint using this function
CREATE TABLE bookreturns (
--...
CONSTRAINT CK_bookreturns_status CHECK (dbo.IsReturnDuplicate(id, bookid, status) == 0)
)
Using Gordon answer
create unique index unq_bookissuedReference
on Books(Book_id) where [status] = 'issued'
Works for me

SQL convert all nullable columns to not nullable columns

I got a database with 200 plus tables.
Now I need a SQL script to find & convert all "nullable Int" to "nonnullable Int" columns (assume database is empty).
I know it can be done manually one by one
ALTER TABLE myTable
ALTER COLUMN myColumn {DataType} NOT NULL DEFAULT(0)
But I think there should be better way to do this, and it will be great help if you can suggest one.
Use this SQL:
DECLARE #TableName NVARCHAR(255)
DECLARE #ColumnName NVARCHAR(255)
DECLARE #Cursor CURSOR
SET #Cursor = CURSOR FAST_FORWARD
FOR SELECT TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'dbo' AND IS_NULLABLE = 'YES' AND DATA_TYPE = 'int'
OPEN #Cursor FETCH NEXT FROM #Cursor INTO #TableName, #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
ALTER TABLE #TableName ALTER COLUMN #ColumnName INT NOT NULL DEFAULT(0)
FETCH NEXT FROM #Cursor INTO #TableName, #ColumnName
END
CLOSE #Cursor
DEALLOCATE #Cursor
Oops...
https://msdn.microsoft.com/ja-jp/library/ms190273.aspx
NULL | NOT NULL
If the new column does not allow null values and the table is not empty, a DEFAULT definition must be added with the new column, and the new column automatically loads with the default value in the new columns in each existing row
and also you can see...
Add a column with a default value to an existing table in SQL Server
Cheers!
No. You can add multiple columns, but you have to use a separate alter table statement for each column you want to alter.
For Reference, See the ALTER TABLE syntax here http://msdn.microsoft.com/en-US/library/ms190273.aspx

Regarding some Update Stored procedure

I have two tables as follows:-
Table1:
-------------------------------------
PageID|Content|TitleID(FK)|LanguageID
-------------------------------------
1 |abc |101 |1
2 |xyz |102 |1
--------------------------------------
Table2:
-------------------------
TitleID|Title |LanguageID
-------------------------
101 |Title1|1
102 |Title2|1
------------------------
I don't want to add duplicates in my Table1 (content table). Like.. there can be no two pages with the same title. What check do I need to add in my Insert/Update Stored Procedure? How do I make sure duplicates are never added.
I have tried as follows:-
CREATE PROC InsertUpdatePageContent
(
#PageID int,
#Content nvarchar(2000),
#TitleID int
)
AS
BEGIN
IF(#PageID=-1)
BEGIN
IF(NOT EXISTS(SELECT TitleID FROM Table1 WHERE LANGUAGEID = #LANGUAGEID))
BEGIN
INSERT INTO Table1(Content,TitleID)
VALUES(#Content,#TitleID)
END
END
ELSE
BEGIN
IF(NOT EXISTS(SELECT TitleID FROM Table1 WHERE LANGUAGEID = #LANGUAGEID))
BEGIN
UPDATE Table1
SET Content = #Content, TitleID = #TitleID
WHERE PAGEID = #PAGEID
END
END
END
Now what is happening is that it is inserting new records alright and won't allow duplicates to be added but when I update its giving me problem.
On my aspx Page I have a drop down list control that is bound to DataSource that returns Table 2 (title table) and I have a text box in which user types Page's content to be stored.
When I update, like lets say I have a row in my Table 1 as shown above with PageID=1.
Now when I am updating this row, like I didn't change the Title from the drop down and only changed Content in the text box, its not updating the record ..and when Stored procedure's Update Query does not execute it displays a Label that says "Page with this title exists already."
So whenever I am updating an existing record that label is displayed on screen.How do I change that IF condition in my Update stored procedure??
EDIT:
#gbn: will that IF condition work in case of update? I mean lets say I am updating the Page with TitleID=1, I changed its content, then when I update, it's gonna execute that IF condition and it still won't update coz TitleID=1 already exits!It will only update if TitleID=1 is not there in Table1. Isn't it? Guess I am getting confused. Please answer. Thanks.
Applying a UNIQUE key constraint on TitleID in Table 1 would ensure that no duplicate values for TitleID get saved into the table.
And as far as your stored procedure's code is concerned, gbn's reply highlights the conditional check to be corrected to make it work.
For Update, you cna put this condition to avoid same titles for multiple pages..
IF NOT EXISTS (SELECT 1 from Table1 where TitleId = #TitleID and PageID <> #PageID) -- This makes sure that there is no 'other page' with same title (updated from UI)
{
UPDATE Table1
SET Content = #Content, TitleID = #TitleID
WHERE PAGEID = #PAGEID
}
This will always return false
IF(NOT EXISTS(SELECT TitleID FROM Table1 WHERE LANGUAGEID=LANGUAGEID))
You are comparing a column with itself, it matches = EXISTS
It should be something like
CREATE PROC InsertUpdatePageContent (
#PageID int,
#Content nvarchar(2000),
#TitleID int
)
AS
IF(#PageID=-1)
BEGIN
IF NOT EXISTS (SELECT TitleID FROM Table1 WHERE TitleID=#TitleID)
INSERT INTO Table1(Content,TitleID) VALUES(#Content,#TitleID)
END
ELSE
UPDATE Table1 SET Content= #Content, TitleID=#TitleID WHERE PAGEID=#PAGEID
GO
To protect your data, you should have unique index on TitleID in Table1.
I am not sure I completely understand, but I'll take a shot at it for you. I will assume you can't change your data structure.
First I would put a unique constraint on the Title column in the Table2. Then I would put a unique constraint on the TitleId (FK) in the Table1. This will ensure that you don't have two entries in Table1 with the same title.
Then in the stored procedure I would do the comparison against the parameters being brought in and what exists in the database so that either update or insert is being validated against what's being passed in.
I normally use stored procedures as an API between the relational data model and the object model for some software. I rarely expose database identity columns to stored procedure signatures for applications. Database identity columns get treated differently than unique identifiers that software might use to identify entities. It's hard to help much more without additional information about the rest of the application and other stored procedures that need to work together for your solution.

Categories

Resources