SQL convert all nullable columns to not nullable columns - c#

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

Related

Migrate Nhibernate Castle.ActiveRecord to EFCore HiLo

We're currently migrating our old OR Mapper to EF Core. Till now we used the
http://www.castleproject.org/projects/activerecord
or mapper with the HiLo algorithm. The explanations is:
https://github.com/castleproject-deprecated/ActiveRecord/blob/master/docs/primary-key-mapping.md
Now we want to switch to EF Core and will try to use the same algorithm. But there isn't much explanation how the HiLo algorithm exactly works in Nhibernate/ActiveRecord. And I try to avoid Id collision.
As far as I see, the Hi value is configured in a Database:
select next_hi from hibernate_unique_key
with the value: 746708
I think the maxLow Value is Int16.MaxValue
In that case the Sequence for EFCore should be:
CREATE SEQUENCE [dbo].[DBSequenceHiLo]
AS [bigint]
START WITH (select next_hi from hibernate_unique_key + Int16.MaxValue)
INCREMENT BY Int16.MaxValue
MINVALUE -9223372036854775808
MAXVALUE 9223372036854775807
CACHE
GO
How does the ActiveRecord HiLo Algorithm exactly works? What is the Increment by value? What is the start with value? The migration will take some time, is it possible to run it parallel with the same HiLo algorithm?
As far as I know. It's not possible to use the exact same algorithm for ActiveRecord and EF Core. One works with Sequence and the other works with a table. So you can't use both OR Mapper parallel. But you can create a Sequence for EF Core without ID collision, you just can't use ActiveRecord afterwards.
To get the INCREMENT BY value just start the current app. Create a DB Entry with the App. Stop it. Start it again and create a second entry. Because you stopped the app, the Lo/cache is empty and it gets the next hi value. The difference between those two ID's is the "INCREMENT BY" value of Active Record. It was 2^17 in my case. Default should be 2^15 I think, but I haven't seen any Infos about it.
To get a start value I created a SQL script, to get the highest Id of the database. Here is my script (It works only if your PK is named Id and it only works with sql.)
DECLARE #tables TABLE(tablename nvarchar(max) NOT NULL);
DECLARE #name nvarchar(max)
DECLARE #maxid bigint
DECLARE #currentid bigint
DECLARE #query nvarchar(max);
DECLARE #sSQL nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
set #maxid = 0
insert into #tables
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_CATALOG = 'BB_Vision'
While(Select Count(*) From #tables) > 0
Begin
Select Top 1 #name = tablename From #tables
IF EXISTS(SELECT 1 FROM sys.columns
WHERE Name = N'Id'
AND Object_ID = Object_ID(#name))
BEGIN
SELECT #sSQL = N'SELECT #retvalOUT = MAX(ID) FROM ' + #name;
SET #ParmDefinition = N'#retvalOUT bigint OUTPUT';
EXEC sp_executesql #sSQL, #ParmDefinition, #retvalOUT=#currentid OUTPUT;
IF #currentid > #maxid
BEGIN
set #maxid = #currentid
END
END
Delete #tables Where #name = tablename
End
select #maxid+1
Now you can create your EF Core Sequence. Here is an explanation how to use it:
https://www.talkingdotnet.com/use-hilo-to-generate-keys-with-entity-framework-core/
After that you shouldn't use ActiveRecord anymore or you have to create your sequence again with a higher start value.
Because the migration takes some time and you will mostly still create some Features/Bugfix for the current OR mapper, it's a good idea to set your ActiveRecord Hi value to a larger value on your local Database. So you can work with both on the same Database. But I wouldn't use it in production
update hibernate_unique_key set next_hi = next_hi + next_hi

Create columns with a loop

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 :)

Table Value optional parameter

Is it possible to create a procedure with a table value parameter as an optional parameter.
I tried the following code:
CREATE PROCEDURE SP
#Table testteype = null READONLY
AS
....
But I get this error:
Operand type clash: void type is incompatible with test type
ps: I use sql server with C#.Net
Table-Valued parameters always have an implicit value of an empty table. So you can actually call that procedure without any parameters and it would execute but the table would be empty.
So it doesn't really make sense to label a table-value parameter with a default value. Remove the "=null", check the table contents, and you should be good to go.
Basically, having default value "= null" makes no sense and is the reason of the error.
By default, #Table testteype gets value of an empty table. Thus, you may remove = null:
CREATE PROCEDURE SP
#Table testteype READONLY
AS
....
Reference: for a sample on how to use this with C# ADO.NET i would recommend to use this post - Using SQL Server’s Table Valued Parameters
--exa:
--create TYPE Table_tt as table(id int,name varchar(10))
create table #a (aid int,price int)
insert into #a (aid ,price )
select 1,10
union
select 2,50
create PROCEDURE SP
#Table_tt Table_tt null READONLY
AS
begin
select * into #tem from #Table_tt
select * from #a where aid in(select id from #tem) or aid=''
end
exec SP
Not sure why the answer above states making default value = NULL is incorrect but this works for me.
CREATE PROCEDURE SP
(
#Param1 VARCHAR(10),
#Param2 VARCHAR(10)=NULL
)
SELECT......
WHERE #Param1 = SOMETHING
AND (#Param2 = SOMETHING OR #Param2 IS NULL)

Why the # (hashes) in Dapper sample

I'm just reading this sample from the Dapper "manual":
connection.Execute(#"
set nocount on
create table #t(i int)
set nocount off
insert #t
select #a a union all select #b
set nocount on
drop table #t", new {a=1, b=2 })
.IsEqualTo(2);
Are the #t's a special syntax for something? Or are they just there to confuse me? :)
Yes, # means something important in TSQL - a table named foo is permenant, for that db/schema. A table named #foo is a temporary table - it only exists for that connection, and is removed when the connection is closed or reset. A table named ##foo is a global temporary table, and exists everywhere, but is intended to be temporary. This is mainly used when bulk-shifting data.
The use of #t here is so that the table only exists on that connection, so we can re-run the test trivially.
Also, a table named #foo is either a table-variable, or a table-valued-parameter, and only exists for that command / sproc.

C# MSSQL alter table then modify values

While the following work fine in SQL Server Management Studio, it just won't work in C#:
DECLARE #PeriodID AS bigint;
SELECT TOP 1 #PeriodID = PeriodID FROM Periods ORDER BY PeriodID DESC;
IF NOT EXISTS(SELECT * FROM information_schema.columns WHERE COLUMN_NAME = N'PeriodID' AND TABLE_NAME = N'MobilePlans')
BEGIN
BEGIN
ALTER TABLE MobilePlans ADD PeriodId bigint NULL
END
BEGIN
UPDATE MobilePlans SET PeriodID = #PeriodID
END
BEGIN
ALTER TABLE MobilePlans ALTER COLUMN PeriodID bigint NOT NULL
END
END
In C#, it keeps telling me Invalid column name 'PeriodID'. and after spending a couple of hours searching, I thought I'd ask here.
While searching, I came across http://bytes.com/topic/c-sharp/answers/258520-problem-sqlcommand-t-sql-transaction, but I couldn't really translate my conditional query to that.
Why can't C# do the same as the Management studio?
Is there another way to do what the query does, that works in C#? I need to perform this on 400 databases, so I'd really like a script to do it for me.
Thanks in advance!
SQL server version is 2008.
Manager version is 2008 (10.0.2531).
.NET framework version is 2.0.
I get "invalid column name 'PeriodID'" running it in Management Studio, if the table doesn't already have the PeriodID column.
Repro:
create table Periods (
PeriodID bigint not null
)
go
insert into Periods(PeriodID) select 1
go
create table MobilePLans (
BLah int not null
)
go
insert into MobilePLans(BLah) select 2
go
DECLARE #PeriodID AS bigint;
SELECT TOP 1 #PeriodID = PeriodID FROM Periods ORDER BY PeriodID DESC;
IF NOT EXISTS(SELECT * FROM information_schema.columns WHERE COLUMN_NAME = N'PeriodID' AND TABLE_NAME = N'MobilePlans')
BEGIN
BEGIN
ALTER TABLE MobilePlans ADD PeriodId bigint NULL
END
BEGIN
UPDATE MobilePlans SET PeriodID = #PeriodID
END
BEGIN
ALTER TABLE MobilePlans ALTER COLUMN PeriodID bigint NOT NULL
END
END
The reason is simple - SQL Server tries to compile each batch completely. If the column already exists, then the UPDATE statement can be compiled. If not, you get the error.
If you put the update inside an Exec:
EXEC('UPDATE MobilePlans SET PeriodID = ' + #PeriodID)
Then it will be compiled at the point that the column does exist.
I guess you have a spelling mistake in your c# code.
The error is saying PeriodeID whereas your column name is PeriodID.
Clarification:
1 - You would like to push this script it against many databases?
2 - Invalid column name 'PeriodeID', I cannot see a column called "PeriodeId", but "PeriodId", is this a typo?
3 - Can you try the same block without main BEGIN/END block?
Is the server collation Case Insensitive ? Because you're adding PeriodId and then updating PeriodID

Categories

Resources