Sqlite - insert into multiple tables with an auto-increment PID - sqlite

I'm using SQLite. I have two tables:
Log:
pid INTEGER PRIMARY KEY AUTOINCREMENT
action INTEGER NOT NULL
.... ect.
ErrorExtras:
pid INTEGER REFERENCES log(pid)
msg TEXT,
request TEXT
Now when an error occurs, I want to insert into both. Inserting into Log is easy, as it generates the PID, however, inserting into ErrorExtras is harder since I don't know the PID without doing a query. If I insert into Log, query for the PID and then insert into ErrorExtras, that seems quie messy. Is there some kind of shortcut for these kinds of inserts?
In my case, this is possible because the other info in log uniquely identifies the pid (so there's a combined key), but what if this weren't the case?

You needn't query for the insert_id, just use the last_insert_id in the select statement in ErrorExtras and Bob's your uncle.

If you have control of your SQL commands, you can use the last_insert_rowid SQL function like this:
INSERT INTO Log(action) VALUES(42);
INSERT INTO ErrorExtras(pid, msg) VALUES(last_insert_rowid(), 'x');
(But this works only for the very next INSERT command, because afterwards, last_insert_rowid() returns the rowid of the ErrorExtras record.)
If you are using the C API, you can use the sqlite3_last_insert_rowid function:
sqlite3_prepare_v2(db, "INSERT INTO Log(action) VALUES(42)", -1, &stmt, NULL);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
sqlite3_prepare_v2(db, "INSERT INTO ErrorExtras(pid,msg) VALUES(?,?)", -1, &stmt, NULL);
sqlite3_bind_int64(stmt, 1, sqlite3_last_insert_rowid(db));
sqlite3_bind_text(stmt, 2, "x", -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
The APIs of other languages typically also have some mechanism to get the last inserted rowid.
For example, in Android, the insert function returns it:
ContentValues cv = new ContentValues();
cv.put("action", 42);
long log_rowid = db.insert("Log", null, cv);
ContentValues cv = new ContentValues();
cv.put("pid", log_rowid);
cv.put("msg", "x");
db.insert("ErrorExtras", null, cv);

Related

Return value to textbox from stored procedure

Having trouble returning the value ID value I need for output back to the textbox in the form. Webforms and ADO.net
I tried adding a param identity as an int and OUT clause, while setting identity = scope_identity and returning the value then using the pattern my team is currently using for ExecuteNonQuery with anonymous parameter classes passing in values and tried passing the #identity value to the textbox.text for the id.
DataManager.Db.ExecuteNonQuery("DefaultConnection", "usp_CreateNewSalesTerritory",
new SqlParameter("#orgId", orgId),
new SqlParameter("#identity", salesTerritoryIdTextBox.Text),
new SqlParameter("#salesTerritoryName", salesTerritories.Name),
new SqlParameter("#createdBy", salesTerritories.CreatedBy),
new SqlParameter("#createdDate", salesTerritories.CreatedDate),
new SqlParameter("#updatedBy", salesTerritories.UpdatedBy),
new SqlParameter("#updatedDate", salesTerritories.UpdatedDate),
new SqlParameter("#isActive", salesTerritories.IsActive));
CREATE PROCEDURE dbo.usp_CreateNewSalesTerritory
#orgId VARCHAR(255),
#salesTerritoryName VARCHAR(255),
#createdBy VARCHAR(255),
#createdDate DATETIME,
#updatedBy VARCHAR(255),
#updatedDate DATETIME,
#isActive BIT,
#identity INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO SalesTerritory (OrganizationId, Name, IsActive,
CreatedBy, CreatedDate, UpdatedBy, UpdatedDate)
VALUES (#orgId, #salesTerritoryName, #isActive,
#createdBy, #createdDate, #updatedBy, #updatedDate);
--SELECT SCOPE_IDENTITY();
--RETURN SCOPE_IDENTITY();
--SELECT ##IDENTITY;
SET #identity = SCOPE_IDENTITY()
END;
RETURN #identity
I expected to get the new inserted ID value for that record, instead, I get the default value of 0 on the screen
Normally, you would call such a stored procedure in "pure" ADO.NET using the .ExecuteNonQuery() method on the SqlCommand object - since it's an INSERT statement.
But now, your stored procedure is actually returning some data - so you really need to treat this like a "normal" SELECT stored procedure.
Assuming you're always returning just the SCOPE_IDENTITY() value - preferably like this:
SELECT SCOPE_IDENTITY();
which is just one single value - you can use the .ExecuteScalar() method on the SqlCommand object - something like this:
object returned = sqlCmd.ExecuteScalar();
if (returned != null)
{
int newIdValue = Convert.ToInt32(returned);
}
// else -> nothing was returned, so most likely no row has been inserted -> handle it appropriately
So maybe you already have a "wrapper" method for .ExecuteScalar() on your DataManager.Db object - or maybe you need to add it. Give it a try - I'm pretty sure this will solve the issue.
I would avoid using the RETURN ... statement - SQL Server stored procedure by default will always return the number of rows that were affected by your stored procedure - don't change that "standard" behavior, if you can.

Get count of rows in sqlite table using c in a program

How to get count of rows in sqlite table using c in a program? Is it only by running "select * from table" ? and incrementing a variable?
sqlQuery = "select count (*) from company;";
rc = sqlite3_prepare_v2(db, sqlQuery, -1, &stmt, NULL);
printf("prepare status for count : %d\n", rc);
rowcount = sqlite3_step(stmt);
sqlQuery = "select * from company;";
rc = sqlite3_prepare_v2(db, sqlQuery, -1, &stmt, NULL);
printf("prepare status for count : %d\n", rc);
while (SQLITE_ROW == sqlite3_step(stmt)) {
rowcount ++;
}
I am not getting the row count with 1,
But I do get it when I perform 2.
The sqlite3_step() function does not return the results of the query. It returns SQLITE_ROW if there is another row in the result set, or SQLITE_DONE if there isn't. It also returns an error code if something bad happened.
sqlQuery = "select count(*) from company;";
rc = sqlite3_prepare_v2(db, sqlQuery, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
// error handling -> statement not prepared
}
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW) {
// error handling -> no rows returned, or an error occurred
}
rowcount = sqlite3_column_int(stmt, 0);
To get the values for the current row in the result set, you have to access them by column index, using the prepared statement that is executing the query. Since there is only one column in this query, the index of 0 is used for the left-most, or only, column in the result. And, since the value of that column is an integer, we use the sqlite3_column_int() function, which returns the value as a 32-bit integer.
For most ordinary database operations, you only need to use a few of the functions in the SQLite C API. You should really read and absorb the introduction. It will help you to understand the normal workflow for database operations.

Android SQLITE Insert into table with values coming from a subquery

In my db-driven app I need to perform insert into queries in which the value for one or more field comes from a subquery.
The insert into statement may look like the following example:
INSERT INTO MyTable (field_1, field_2)
VALUES('value for field 1', (SELECT field_x FROM AnotherTable WHERE ...))
At present I am doing it manually building the query:
String MyQuery = "INSERT INTO mytable (field_1, field_2)
VALUES('value for field 1', (SELECT field_x FROM AnotherTable WHERE ...))"; // Of course my query is far more complex and is built in several steps but the concept is safe, I end up with a SQL String
SQLiteDatabase= db = getWritableDatabase();
db.execSQL(MyQuery); // And it works flawlessy as it was a Swiss Clock
What i would like to do instead is:
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put("field_1", "value for field 1");
values.put("field_2", ThisIsAQuery("(SELECT field_x FROM AnotherTable WHERE ...)"));
db.insert("MyTable", null, values);
db.close();
Where the fake method ThisIsAQuery(...) is the missing part, something that should tell the query builder that "SELECT.." is not a value but a query that should be embedded in the insert statement.
Is there a way to achieve this?
The whole point of the ContentValues container is to be able to safely use strings without interpreting them as SQL commands.
It is not possible to use subqueries with insert(). The only way to get a value from another table is by executing a separate query; in this case, ThisIsAQuery() would be stringForQuery() or longForQuery().

INSTEAD OF INSERT trigger causes error in SaveChanges of Entity Framework

This is my first post, but I come here a lot for finding the right solutions for my problems.
I have a database (SQL Server 2008 R2) with a table that has an INSTEAD OF INSERT trigger for checking the inserted data.
If it's a duplicated row, update that row; otherwise, insert that row.
Trigger:
ALTER TRIGGER [dbo].[CheckDataTrigger]
ON [dbo].[MonitorSummary]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT OFF
IF exists( select * from inserted i, MonitorSummary ms
where ms.ServiceUrl = i.ServiceUrl
AND ms.AppName = i.AppName
AND ms.BuildVersion = i.BuildVersion
AND ms.FunctionName = i.FunctionName
AND ms.Company = i.Company
AND ms.UserName = i.UserName
AND ms.ServerName = i.ServerName)
BEGIN
UPDATE MonitorSummary
SET MonitorSummary.Function_Count = inserted.Function_Count,
MonitorSummary.Execution_Time_Sum = inserted.Execution_Time_Sum,
MonitorSummary.LogLevel = inserted.LogLevel
FROM inserted
WHERE MonitorSummary.ServiceUrl = inserted.ServiceUrl
AND MonitorSummary.AppName = inserted.AppName
AND MonitorSummary.BuildVersion = inserted.BuildVersion
AND MonitorSummary.FunctionName = inserted.FunctionName
AND MonitorSummary.Company = inserted.Company
AND MonitorSummary.UserName = inserted.UserName
AND MonitorSummary.ServerName = inserted.ServerName
END
ELSE
BEGIN
INSERT INTO MonitorSummary
SELECT
i.ServiceUrl, i.EmailAdress, i.EmailSent, i.AppName, i.BuildVersion,
i.FunctionName, i.Company, i.UserName, i.ServerName, i.Function_Count,
i.Execution_Time_Sum, i.LogLevel
FROM inserted i
END
SELECT Monitor_Id
FROM MonitorSummary
WHERE ##ROWCOUNT > 0 AND Monitor_Id = SCOPE_IDENTITY()
END
As you can see, SET NOCOUNT OFF and I do the select query at the end of the trigger.
Extra information about the table:
CREATE TABLE [dbo].[MonitorSummary](
[Monitor_Id] [int] IDENTITY(1,1) NOT NULL,
[ServiceUrl] [nvarchar](100) NULL,
[EmailAdress] [nvarchar](100) NULL,
[EmailSent] [bit] NOT NULL default 0,
[AppName] [nvarchar](100) NULL,
[BuildVersion] [nvarchar](100) NULL,
[FunctionName] [nvarchar](100) NULL,
[Company] [nvarchar](100) NULL,
[UserName] [nvarchar](100) NULL,
[ServerName] [nvarchar](100) NULL,
[Function_Count] [int] NOT NULL default 0,
[Execution_Time_Sum] [numeric](18, 8) NOT NULL default 0,
[LogLevel] [nvarchar](50) NULL,
PRIMARY KEY CLUSTERED
(
[Monitor_Id] ASC
)
I have tested the trigger before using it. it works great!
Test code:
INSERT INTO MonitorSummary VALUES
('ServiceUrl2', 'EmailAdress2', 0, 'AppName2', 'BuildVersion2', 'FunctionName2',
'Company2', 'UserName2', 'ServerName2', 1, 1, 'LogLevel2'),
('ServiceUrl2', 'EmailAdress2', 0, 'AppName2', 'BuildVersion2', 'FunctionName2',
'Company3', 'UserName2', 'ServerName2', 1, 1, 'LogLevel2'),
('ServiceUrl3', 'EmailAdress3', 0, 'AppName2', 'BuildVersion2', 'FunctionName2',
'Company2', 'UserName2', 'ServerName2', 1, 1, 'LogLevel2'),
('ServiceUrl3', 'EmailAdress3', 0, 'AppName2', 'BuildVersion2', 'FunctionName2',
'Company2', 'UserName2', 'ServerName2', 2, 2, 'LogLevel2')
NOW, the problem is that the EF (Entity Framework) doesn't work.
I think it's irrelevant that this works in a web service (.asmx), because I tested EF with a test table (without the trigger) and everything just works fine.
In this code, I add some objects to EF and save the changes.
Already tried to save the changes in the loop, but this it has no effect...
private void StoreData(MonitorSummary[] data)
{
foreach (MonitorSummary item in data)
{
MonitorEntity.MonitorSummary.AddObject(item);
//MonitorEntity.SaveChanges();
}
MonitorEntity.SaveChanges();
}
I always end with this error:
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since
entities were loaded. Refresh ObjectStateManager entries.
I have also tried to Attach the object and change the concurrency mode to fixed,
all of this doesn't work..
I even tried an AFTER INSERT trigger, but that is not the solution I wanted and I have to code a lot more since it's already inserted in the table..
Does anyone now a solution to my problem?
I believe that you need to set ##identity in UPDATE part of the trigger. To do so, create a simple insert query and execute it. Assuming there is identity column id in MonitorSummary:
declare #strSql varchar(1000)
declare #id int
select #id = max (id)
from MonitorSummary
where -- whole join condition to identify changed record(s)
set #strSql = 'SELECT Identity (Int, ' + Cast(#id As Varchar(10)) + ',1) AS id
INTO #Tmp'
EXECUTE (#strSql)
If you use Sql Server 2005 or newer there is output clause in UPDATE you can use to retrieve list of changed records.
Btw, you might remove exists part and check if UPDATE set ##rowcount to zero; if it did, you need to insert rows.

qt sqlite insert into autoincrement table yields two rows

Hi
I have a sqlite db which I am manipulating using qts built in sqlite database driver.
I have a small test app that allows me to run an sql query from a line edit and it will be executed and the results are then updated in a view of the relevant model.
I have created a table which uses autoincremented primary key values, but if I execute an insert statement without providing the key, I get two rows inserted, each with an autoincremented value.
If I provide the key value, only one row is created. Any ideas why this is?
Table is simple enough, e.g
CREATE TABLE GroupNames ( ID integer PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, Name varchar(50))
and when I run the query
insert into groupnames (name) values ("testName");
I get two new rows with autoincremented ids. However, if I run
insert into groupnames (id, name) values (100, "testName");
I get one row as expected, with the correct id 100.
Also of note is that if I try
insert into table groupnames (id, name) values (100, "testName");
insert into table groupnames (name) values ("testName");
the query does not run.
The qt code to run the query could not be simpler:
QSqlQuery *DbCore::run_query(const QString &query_string)
{
QSqlDatabase db = QSqlDatabase::database(defConnectionName);
if(!db.isOpen())
return NULL;
QSqlQuery *q = new QSqlQuery(query_string, db);
q->exec();
return q;
}
I have added some logging code to check that the query is executed once:
QSqlDatabase db = QSqlDatabase::database(defConnectionName);
if(!db.isOpen())
return NULL;
qDebug() << "Running query:" << query_string;
QSqlQuery *q = new QSqlQuery(query_string, db);
if(!q->exec())
qDebug() << "Error running query:" << q->lastError();
return q;
The log confirms that I'm only executing once:
Running query: "insert into groupnames (name) values ("hello")"
If i then check the database using sqlite3 shell (to remove any doubt about qt views etc):
sqlite> select * from groupnames;
1|hello
2|hello
question was answered above in a comment:
As i see in the documentation, when you create a QSqlQuery the way you do, the query, if not empty, is executed. To create the QSqlQuery and execute the query, use this: QSqlQuery *q = new QSqlQuery(db); q->exec(query_string) To see the last executed query, use QSqlQuery::lastQuery() And for the last query that was successfully executed QSqlQuery::executedQuery() Hope this helps. – Hector Mar 16 at

Resources