"There was an error parsing the query. SQL Server CE - c#

I wrote an little app in C# to keep track of customers and jobs but I get an error
"There was an error parsing the query.[Token line number = 1, Token
line offset = 14,Token in error = ']
I'm using a SQL Server CE .sdf database
public static void CreateEmployeeTable(string name)
{
conn.Open();
using (SqlCeCommand cmd = conn.CreateCommand())
{
cmd.CommandText = #"CREATE TABLE `#Name` (
`Id` INT(10) NOT NULL AUTO_INCREMENT,
`Job` VARCHAR(50) NULL,
`Pay` VARCHAR(50) NULL,
`TotalJobs` VARCHAR(50) NULL,
`TotalPay` VARCHAR(50) NULL,
PRIMARY KEY (`Id`)";
cmd.Parameters.AddWithValue("#Name", name);
cmd.ExecuteNonQuery();
}
}
I researched the error a lot and seems this error is pretty vague

You cannot parameterise a CREATE table statement that way.
Simply construct the CommandText string in code using string concatenation (being careful of possible SQL Injection vulnerabilities) and execute it.

You cannot parameterize the table name in your example. I altered your create table SQL to conform to supported data types for SQL CE. Please note that the NVARCHAR statements are set to a length of 100 to support up to the 50 characters you were specifying (VARCHAR is not supported).
I tested this code sample, but there are a couple more things to think about:
Your method is currently public. I don't know the context in which you are calling this, but consider making it private or internal.
Consider querying first to determine whether the table already exists to avoid an exception.
If at all possible I would create the connection in a using block directly above your command creation. That way you will know the connection gets closed and disposed. I'll leave that to your discretion.
public static void CreateEmployeeTable(string name)
{
const string createTableFormat = #"CREATE TABLE [{0}] (
Id INT IDENTITY NOT NULL PRIMARY KEY,
Job NVARCHAR(100) NULL,
Pay NVARCHAR(100) NULL,
TotalJobs NVARCHAR(100) NULL,
TotalPay NVARCHAR(100) NULL)";
if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException("name");
}
// Just replace with your connection string.
using (SqlCeConnection cn = new SqlCeConnection(Settings.Default.LocalDbConnectionString))
using (SqlCeCommand cmd = cn.CreateCommand())
{
cmd.CommandText = string.Format(createTableFormat, name);
cn.Open();
cmd.ExecuteNonQuery()
}
}

Related

There was an error parsing the query. How to resolve it?

I want to run sql statement which I frist read from .sql file.
I'm getting this error:
{"There was an error parsing the query. [ Token line number = 13,Token line offset = 1,Token in error = ALTER ]"}
There is my sql statement in .sql file:
CREATE TABLE [Test]
(
[Id] INT NOT NULL IDENTITY (1,1),
[DatabaseVersion] NVARCHAR(20) NOT NULL,
[Autorun] BIT,
[CurrentCulture] NVARCHAR(10),
[MailNotificationEnabled] BIT,
[RefreshInterval] INT,
[ModifiedDate] DATETIME NOT NULL,
[schemat] NVARCHAR(255)
)
ALTER TABLE [Test] ADD CONSTRAINT [PK_Test] PRIMARY KEY ([Id])
UPDATE [AppConfig]
SET [DatabaseVersion] = '0.12'
Reading file:
string oldVersion = GetOldDatabaseVersion();
string sqlScript = "";
sqlScript = GetScriptFromAssembly(oldVersion,
ConfigurationSettings.ValidDatabaseVersion);
ExecuteNonQuery(CommandType.Text, sqlScript);
ExecuteNonQuery method:
public int ExecuteNonQuery(CommandType type, string sql)
{
using (SqlCeConnection connection = CreateConnection())
{
return ExecuteNonQuery(connection, type, sql);
}
}
private int ExecuteNonQuery(SqlCeConnection connection, CommandType type, string sql)
{
using (SqlCeCommand command = new SqlCeCommand())
{
command.Connection = connection;
command.CommandType = type;
command.CommandText = sql;
return command.ExecuteNonQuery();
}
}
I doesn't know how to resolve it. When I run script dirctly on db it works.
Solved:
string sqlScript = GetScriptFromAssembly(GetOldDatabaseVersion(), ConfigurationSettings.ValidDatabaseVersion);
string[] scripts = sqlScript.Split(new string[] { #"/*$$*/" }, StringSplitOptions.None);
foreach(var script in scripts)
ExecuteNonQuery(CommandType.Text, script);
Added delimiters to sql:
CREATE TABLE [Test]
(
[Id] INT NOT NULL IDENTITY (1,1),
[DatabaseVersion] NVARCHAR(20) NOT NULL,
[Autorun] BIT,
[CurrentCulture] NVARCHAR(10),
[MailNotificationEnabled] BIT,
[RefreshInterval] INT,
[ModifiedDate] DATETIME NOT NULL,
[schemat] NVARCHAR(255)
)
/*$$*/
ALTER TABLE [Test] ADD CONSTRAINT [PK_Test] PRIMARY KEY ([Id])
/*$$*/
UPDATE [AppConfig]
SET [DatabaseVersion] = '0.12'
You must run each statement as a seperate command, you can use code like the helper function to seperate into commands if you seperate each command with GO:
https://github.com/ErikEJ/SqlCeToolbox/blob/master/src/API/Repositories/ServerDBRepository.cs#L639
As you can see I am using SqlCommandReaderStreamed from the DbUp package to do this
Please use the Go keyword before the Alter the table

Sybase Bulk Copy via DLL: Value does not fall within the expected range

I am trying to migrate data from Sql Server to a Sybase 16.0 database using the .net DLLs supplied with the Sybase installation (Sybase.AdoNet4.AseClient.dll version 16.0.02).
To keep things simple I am trying to copy values from a table with a single INT column
--source table (MSSQL)
CREATE TABLE [dbo].[TO_INTS](
[TO_INT] [int] NULL,
[TO_INT2] [int] NULL,
[NAME] [varchar](50) NULL,
[DT] [datetime] NULL
) ON [PRIMARY]
to
--target table (Sybase)
CREATE TABLE dbo.TO_INTS
(
FROM_INT INT NOT NULL
)
ON 'default'
I am using the code:
public void BulkCopyFromSqlServer(string sourceConnectionString, string targetConnectionString)
{
SqlConnection sourceConnection = null;
AseConnection targetConnection = new AseConnection(targetConnectionString);
IDataReader dataSource=null;
try
{
targetConnection.Open();
MssqlCommand.GetDataReader(sourceConnectionString, out sourceConnection, out dataSource); //see below
AseBulkCopy blk = new AseBulkCopy(targetConnection);
blk.BulkCopyTimeout = 1200;
blk.DestinationTableName = "TO_INTS";
blk.ColumnMappings.Clear();
blk.ColumnMappings.Add(new AseBulkCopyColumnMapping(0,0));
blk.WriteToServer(dataSource); // System.ArgumentException thrown here.
blk.Close();
}
catch (AseException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
sourceConnection.Dispose();
targetConnection.Dispose();
}
}
//MssqlCommand.GetDataReader(sourceConnectionString, out sourceConnection, out dataSource):
public static void GetDataReader(string sqlServerConnectionString, out SqlConnection conn, out IDataReader reader)
{
conn = new SqlConnection(sqlServerConnectionString);
conn.Open();
SqlCommand cmd = new SqlCommand("select * from TO_INTS", conn);
cmd.CommandTimeout = 60;
reader = cmd.ExecuteReader();
}
A System.ArgumentException is thrown at when WriteToServer() is called with the message "Value does not fall within the expected range".
The stack trace is interesting in that it looks like the Sybase DLL cannot resolve the DB columnn name using the index provided in the mapping, which seems odd:
at Sybase.Data.AseClient.AseBulkCopy.GetDBColName(String clientColName, Int32 clientColInx)
at Sybase.Data.AseClient.AseBulkCopy.GenerateInsertCmdByReaderMetaInfo(DataTable rowFmt)
at Sybase.Data.AseClient.AseBulkCopy.WriteToServer(IDataReader reader)
I have followed the same process for Sybase > Sql Server (pretty much line by line, but with the relevant DLLs switched) and this works.
Am I missing something obvious?
It looks like I have fixed this now.
There were two errors in my code.
The initial error arose because I did not know about putting the EnableBulkLoad parameter in the connection string. My working connection string looks like this:
string SybaseConnectionString = "Data Source=server1;Initial Catalog=mydb;persist security info=False;User Id=sa;Password=password1;Port=5000;EnableBulkLoad=2"
Once that was added a second error was raised:
Bad row data received from the client while bulk copying into object 2080007410 partition 2080007410 in database 6. Received a row of length 11 whilst maximum or expected row length is 6.
This arose because the table name was set using:
blk.DestinationTableName = "TO_INTS";
when it should have been:
blk.DestinationTableName = "dbo.TO_INTS";
Once I added the owner then the BulkCopy worked.
For ref, now that I have got it working I am able to complete a WriteToServer call between tables with different names. Further, the column names are unique in each table, ie, source Sql Server table:
CREATE TABLE [dbo].[SOURCE_INTS](
[TO_INT] [int] NULL,
[TO_INT2] [int] NULL,
[NAME] [varchar](50) NULL,
[DT] [datetime] NULL
) ON [PRIMARY]
Target Sybase table:
CREATE TABLE dbo.TO_INTS
(
THE_STRING VARCHAR(50) NOT NULL,
THE_INT INT NOT NULL,
THE_DT DATETIME NOT NULL
)
LOCK ALLPAGES
ON 'default'
GO
You will also notice that the order differs but WriteToServer handles this fine with mappings:
blk.ColumnMappings.Add(new AseBulkCopyColumnMapping(2, 0)); //string col
blk.ColumnMappings.Add(new AseBulkCopyColumnMapping(1, 1)); //int col
blk.ColumnMappings.Add(new AseBulkCopyColumnMapping(3, 2)); //datetime col
If you need more info on the Sybase Data Provider for C#, try Sybase Books Online
Hope this helps someone.
I don't have any immediate observations other than this one: your source table in MS SQL is different from the target table in ASE in two ways:
it has less columns and
the column name in ASE is different from the column names in MS SQL.
Without understanding the full scope and context of what goes on here, it would not be hard to imagine a mismatch occurring between source and target table.

Select NULL values from SQL Server table

I am working with ASP.NET MVC 4 using C# and SQL Server
I am selecting a row of data from the following table
CREATE TABLE [dbo].[Mem_Basic] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Mem_NA] VARCHAR (100) NOT NULL,
[Mem_Occ] VARCHAR (200) NOT NULL,
[Mem_Role] VARCHAR (200) NOT NULL,
[Mem_Email] VARCHAR (50) NULL,
[Mem_MPh] VARCHAR (15) NULL,
[Mem_DOB] DATE NULL,
[Mem_BGr] NCHAR (10) NULL,
[Mem_WAnn] DATE NULL,
[Mem_Spouse] VARCHAR (75) NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
using the following code
public MemberBasicData GetMemberProfile(int id)
{
MemberBasicData mb = new MemberBasicData();
using (SqlConnection con = new SqlConnection(Config.ConnectionString))
{
using (SqlCommand cmd = new SqlCommand("SELECT * FROM Mem_Basic WHERE Id="+id+"", con))
{
try
{
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
if(reader.Read()==true)
{
mb.Id = (int)reader["Id"];
mb.Mem_NA = (string)reader["Mem_NA"];
mb.Mem_Occ = (string)reader["Mem_Occ"];
mb.Mem_Role = (string)reader["Mem_Role"];
mb.Mem_Email = (string)reader["Mem_Email"];
mb.Mem_MPh = (string)reader["Mem_MPh"];
mb.Mem_DOB = (Convert.ToDateTime(reader["Mem_DOB"]));
mb.Mem_BGr = (string)reader["Mem_BGr"];
mb.Mem_WAnn = (Convert.ToDateTime(reader["Mem_WAnn"]));
mb.Mem_Spouse = (string)reader["Mem_Spouse"];
}
}
catch (Exception e) { throw e; }
finally { if (con.State == System.Data.ConnectionState.Open) con.Close(); }
}
}
return mb;
}
This shows the error
Unable to cast object of type 'System.DBNull' to type 'System.String'.
(Mem_Email, MPh.. etc sometimes contain a NULL value.. if the value is null I want return null). Anybody please help me.
Just make some short if, you should do the same for all the other variables:
mb.Mem_Email = reader["Mem_Email"] == System.DBNull.Value ? null : (string) reader["Mem_Email"];
You could save yourself a serious amount of pain here with a tool like dapper (http://www.nuget.org/packages/Dapper):
public MemberBasicData GetMemberProfile(int id)
{
using (var con = new SqlConnection(Config.ConnectionString))
{
return con.Query<MemberBasicData>(
"SELECT * FROM Mem_Basic WHERE Id=#id",
new { id } // full parameterization, done the easy way
).FirstOrDefault();
}
}
things this does:
does correct parameterization (for both performance and safety), but without any inconvenience
does all the materialization, handling nulls (both in parameters and columns) for you
is insanely optimized (basically, it is measurably the same speed as writing all that code yourself, except fewer things to get wrong)
Alternatively to King King's answer you can write code like this:
mb.Mem_Email = reader["Mem_Email"] as string;
For value types, if the column allows nulls, it's a good practice to map them to nullable value types in C# so that this code reader["Mem_DOB"] as DateTime? works
Change for all columns, that might be NULL from this
mb.Mem_NA = (string)reader["Mem_NA"];
to that
mb.Mem_NA = reader["Mem_NA"].ToString();
Treat the nullable fields:
mb.Mem_Email = System.DBNull.Value.Equals(reader["Mem_Email"])?"":
(string)reader["Mem_Email"];
Do the same for:
mb.Mem_MPh, mb.Mem_BGr and mb.Mem_Spouse.
I don't mean to sound like a SQL bigot (which of course means I DO mean to sound like a SQL bigot), but if you followed SQL best practices and used a column list instead of SELECT * you could resolve this problem by using COALESCE on the nullable columns thus:
SELECT
[Id],
[Mem_NA],
[Mem_Occ],
[Mem_Role],
COALESCE( [Mem_Email], '' ) AS [Mem_Email],
COALESCE( [Mem_MPh], '' ) AS [Mem_MPh],
COALESCE( [Mem_DOB], CAST( '1753-1-1' AS DATE ) ) AS [Mem_DOB],
COALESCE( [Mem_BGr, '' ) AS [Mem_BGr],
COALESCE( [Mem_WAnn], CAST( '1753-1-1' AS DATE ) ) AS [Mem_WAnn],
COALESCE( [Mem_Spouse], '' ) AS [Mem_Spouse]
FROM
[dbo].[Mem_Basic];
Your c# code can now dependably process the result set without having to account for outliers (the exception being the dates; you should probably check for whatever default you use in the COALESCE for those (I used the minimum allowable value for a SQL Date variable in the above example), and handle them appropriately.
Additionally, you can get rid of the finally block in your c# code. You wrapped the connection in a "using" block; it will automatically close the connection when you go out of scope (that is the purpose of the "using" block).

Inserting a ".01" into database c#

I am trying to insert "0000.0000.0000.0001" into a varchar(50) in my sql database in c#. I keep getting this error:
There was an error parsing the query. [ Token line number = 1,Token line offset = 122,Token in error = .0000 ]
How my insertion looks like is:
string version = "0000.0000.0000.0001";
INSERT INTO [DBFileInfo] (DBFileInfoID,DatabaseType,Version, Description, CreationDate,ReleaseDate)
values(1,1,"+version.ToString()+
",`Default Beverage DB created by DbGen`,
CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)";
The initial build of the table is:
string info = " CREATE TABLE [DBFileInfo]( [DBFileInfoID] INT NOT NULL,[DatabaseType] INT NOT NULL DEFAULT(-1),[Version] VARCHAR(50) NOT NULL, [Description] NVARCHAR(256) NOT NULL, [CreationDate] DATETIME,[ReleaseDate] DATETIME);";
is there a way around this so I could insert the "." into it without getting an exception?
THanks!
You need to put the string inside single quotes:
string version = "0000.0000.0000.0001";
...values(1,1,'" + version + "','...
HOWEVER
I strongly recommend you use parameters instead. e.g.
string version = "0000.0000.0000.0001";
string sql = #"INSERT INTO [DBFileInfo] (DBFileInfoID,DatabaseType,Version, Description, CreationDate,ReleaseDate)
VALUES(1,1,#Version,'Default Beverage DB created by DbGen',CURRENT_TIMESTAMP,CURRENT_TIMESTAMP);";
using (var connection = new SqlConnection(yourConnectionString))
using (var command = new SqlCommand(sql, connection))
{
command.Parameters.AddWithValue("#Version", version);
connection.Open();
command.ExecuteNonQuery();
}
Probably you missed quotation marks, try:
... values(1,1,'"+version.ToString()+"','Default Beverage ..
Though you might be using parameter
... values(1,1,#version,'Default Beverage ..
in this case you will have to execute your statement with parameter #version and pass value to it.

Why does CONVERT() allow me to use parameterized query in this case?

During the development of an windows mobile application we used the following code:
const string sqlString2 =
#"INSERT INTO WorkOrderComment
(WOInstructionID,WorkOrderCommentID,ClientChange,Void,UserID,VoidTimestamp)
(SELECT WOInstructionID, WOInstructionID, 1, #VoidBit,#VoidUser ,#VoidTimeStamp
FROM WorkOrderInstruction
WHERE InstructionGroupQuestion = 0 AND InstructionGroupNumber = #insGroupNo AND WorkOrderID=#WoID)";
var sqlcom2 = new SqlCeCommand();
sqlcom2.CommandType = CommandType.Text;
sqlcom2.CommandText = sqlString2;
sqlcom2.Parameters.Add("#VoidBit", action);
sqlcom2.Parameters.Add("#VoidUser", DBNull.Value);
sqlcom2.Parameters.Add("#VoidTimeStamp", DBNull.Value);
sqlcom2.Parameters.Add("#WoID", workOrderID);
sqlcom2.Parameters.Add("#insGroupNo", instructionGroupNumber);
sqlcom2.ExecuteNonQuery();
When running this with I receive the following error on ExecuteNonQuery:
The conversion is not supported. [ Type to convert from (if known) = int, Type to convert to (if known) = uniqueidentifier ]
Even if I add sqlcom2.Parameters["#VoidUser"].DbType = DbType.Guid; I got the same error. After a while I found that this could be helped with using CONVERT.
I changed #VoidUser to CONVERT(uniqueidentifier,#VoidUser).
This resulted in the following error: A parameter is not allowed in this location. Ensure that the '#' sign is in a valid location or that parameters are valid at all in this SQL statement.
After a couple of hours of reading documentation and other posts on why I could not use variables at these places I just tried to put in CONVERT everywhere; changing #VoidBit to CONVERT(bit,#VoidBit) and #VoidTimeStamp to CONVERT(datetime,#VoidTimeStamp).
And for some reason it works.
The table is created with this command:
CREATE TABLE WorkOrderComment ( WorkOrderCommentID uniqueidentifier NOT NULL CONSTRAINT WorkOrderCommentPK Primary Key,
WOInstructionID uniqueidentifier NOT NULL,
WorkOrderID_Update uniqueidentifier NULL,
TextData NTEXT NULL,
Value float NULL,
Category nvarchar(50) NULL,
BarcodeScanned bit NULL,
Timestamp DateTime NULL,
Time float NULL,
UserID uniqueidentifier NULL,
Void bit NULL,
VoidTimeStamp DateTime NULL,
FaultComplaintID uniqueidentifier NULL,
ClientChange bit NOT NULL)
Why am I not allowed to use "naked" parameters?
Why did it throw an error on the conversion in the first place, and not the usage of a parameter?
The Parameters.Add() method takes 2 parameters: Parameter name and data type. It seems you are confusing with Parameters.AddWithValue()
Also SQLCE seems to be more difficult with type conversion than SQL Server. So the best is you specifically mention the type of the parameters. (it only really matters for uniqueidentifier though)
I would slightly rewrite the code like this as well:
using (var sqlConn = new SqlCeConnection(connStr))
using (var sqlCmd = new SqlCeCommand(sqlString2, sqlConn))
{
sqlConn.Open();
sqlCmd.Parameters.Add("#VoidBit", SqlDbType.Bit).Value = action;
sqlCmd.Parameters.Add("#VoidUser", SqlDbType.UniqueIdentifier).Value = DBNull.Value;
sqlCmd.Parameters.Add("#VoidTimeStamp", SqlDbType.DateTime).Value = DBNull.Value;
sqlCmd.Parameters.Add("#WoID", SqlDbType.UniqueIdentifier).Value = workOrderID;
sqlCmd.Parameters.Add("#insGroupNo", SqlDbType.Int).Value = instructionGroupNumber;
sqlCmd.ExecuteNonQuery();
}
See https://gist.github.com/1932722 for full repro script

Categories

Resources