Declare variable in SQLite and use it - sql

I want to declare a variable in SQLite and use it in insert operation.
Like in MS SQL:
declare #name as varchar(10)
set name = 'name'
select * from table where name = #name
For example, I will need to get last_insert_row and use it in insert.
I have found something about binding but I didn't really fully understood it.

SQLite doesn't support native variable syntax, but you can achieve virtually the same using an in-memory temp table.
I've used the below approach for large projects and works like a charm.
/* Create in-memory temp table for variables */
BEGIN;
PRAGMA temp_store = 2; /* 2 means use in-memory */
CREATE TEMP TABLE _Variables(Name TEXT PRIMARY KEY, RealValue REAL, IntegerValue INTEGER, BlobValue BLOB, TextValue TEXT);
/* Declaring a variable */
INSERT INTO _Variables (Name) VALUES ('VariableName');
/* Assigning a variable (pick the right storage class) */
UPDATE _Variables SET IntegerValue = ... WHERE Name = 'VariableName';
/* Getting variable value (use within expression) */
... (SELECT coalesce(RealValue, IntegerValue, BlobValue, TextValue) FROM _Variables WHERE Name = 'VariableName' LIMIT 1) ...
DROP TABLE _Variables;
END;

For a read-only variable (that is, a constant value set once and used anywhere in the query), use a Common Table Expression (CTE).
WITH const AS (SELECT 'name' AS name, 10 AS more)
SELECT table.cost, (table.cost + const.more) AS newCost
FROM table, const
WHERE table.name = const.name
SQLite WITH clause

Herman's solution works, but it can be simplified because Sqlite allows to store any value type on any field.
Here is a simpler version that uses one Value field declared as TEXT to store any value:
CREATE TEMP TABLE IF NOT EXISTS Variables (Name TEXT PRIMARY KEY, Value TEXT);
INSERT OR REPLACE INTO Variables VALUES ('VarStr', 'Val1');
INSERT OR REPLACE INTO Variables VALUES ('VarInt', 123);
INSERT OR REPLACE INTO Variables VALUES ('VarBlob', x'12345678');
SELECT Value
FROM Variables
WHERE Name = 'VarStr'
UNION ALL
SELECT Value
FROM Variables
WHERE Name = 'VarInt'
UNION ALL
SELECT Value
FROM Variables
WHERE Name = 'VarBlob';

Herman's solution worked for me, but the ... had me mixed up for a bit. I'm including the demo I worked up based on his answer. The additional features in my answer include foreign key support, auto incrementing keys, and use of the last_insert_rowid() function to get the last auto generated key in a transaction.
My need for this information came up when I hit a transaction that required three foreign keys but I could only get the last one with last_insert_rowid().
PRAGMA foreign_keys = ON; -- sqlite foreign key support is off by default
PRAGMA temp_store = 2; -- store temp table in memory, not on disk
CREATE TABLE Foo(
Thing1 INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
);
CREATE TABLE Bar(
Thing2 INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
FOREIGN KEY(Thing2) REFERENCES Foo(Thing1)
);
BEGIN TRANSACTION;
CREATE TEMP TABLE _Variables(Key TEXT, Value INTEGER);
INSERT INTO Foo(Thing1)
VALUES(2);
INSERT INTO _Variables(Key, Value)
VALUES('FooThing', last_insert_rowid());
INSERT INTO Bar(Thing2)
VALUES((SELECT Value FROM _Variables WHERE Key = 'FooThing'));
DROP TABLE _Variables;
END TRANSACTION;

To use the one from denverCR in your example:
WITH tblCTE AS (SELECT "Joe" AS namevar)
SELECT * FROM table, tblCTE
WHERE name = namevar
As a beginner I found other answers too difficult to understand, hope this works

Creating "VARIABLE" for use in SQLite SELECT (and some other) statements
CREATE TEMP TABLE IF NOT EXISTS variable AS SELECT '2002' AS _year; --creating the "variable" named "_year" with value "2002"
UPDATE variable SET _year = '2021'; --changing the variable named "_year" assigning "new" value "2021"
SELECT _year FROM variable; --viewing the variable
SELECT 'TEST', (SELECT _year FROM variable) AS _year; --using the variable
SELECT taxyr FROM owndat WHERE taxyr = (SELECT _year FROM variable); --another example of using the variable
SELECT DISTINCT taxyr FROM owndat WHERE taxyr IN ('2022',(SELECT _year FROM variable)); --another example of using the variable
DROP TABLE IF EXISTS variable; --releasing the "variable" if needed to be released

After reading all the answers I prefer something like this:
select *
from table, (select 'name' as name) const
where table.name = const.name

Try using Binding Values. You cannot use variables as you do in T-SQL but you can use "parameters". I hope the following link is usefull.Binding Values

I found one solution for assign variables to COLUMN or TABLE:
conn = sqlite3.connect('database.db')
cursor=conn.cursor()
z="Cash_payers" # bring results from Table 1 , Column: Customers and COLUMN
# which are pays cash
sorgu_y= Customers #Column name
query1="SELECT * FROM Table_1 WHERE " +sorgu_y+ " LIKE ? "
print (query1)
query=(query1)
cursor.execute(query,(z,))
Don't forget input one space between the WHERE and double quotes
and between the double quotes and LIKE

Related

How to return all records using IN clause when parameter is empty?

I'm passing a parameter like '1,2,3' to this statement:
SELECT *
FROM Negative
WHERE IdNegative IN (#IdNegative)
But I want to return all records (like the records with an id 1,2,3,4,5,6...) when the parameter is empty. Is there a way to do this?
Thank you.
Parameters and variables are not simply pasted into the query by the compiler. They are inserted as actual compiled values, so in this case the string 1,2,3 is never going to be equal to any of the numbers 1 2 3.
You need to declare as a table variable or as a Table-Valued Parameter
DECLARE #IdNegative TABLE (id int PRIMARY KEY);
INSERT #IdNegative VALUES (1),(2),(3);
SELECT *
FROM Negative
WHERE IdNegative IN (SELECT id FROM #IdNegative);

How can I replace multiple words of a string in SQL

Is it possible to replace multiple words in a string in sql without using multiple replace functions?
For example I have a string where I need to replace word 'POLYESTER' with 'POLY' , 'COTTON' with 'CTN', 'GRAPHIC' with 'GRPHC' etc in order to keep the string length at a max of say 30 without much loosing the readability of contents in it(can't use substring to limit chars since it can trim the end meaningful parts of string completely). So we decided to short some keywords like above.
Current query I have used :
SELECT
REPLACE(REPLACE('**Some string value **COTTON **Some string value ** POLYESTER', 'POLYESTER', 'POLY'), 'COTTON', 'CTN')
If I have 10 keywords like this, what will be the best way to achieve the result other than using multiple replace function. I am using SQL Server 2012.
considering sql server is your only instrument (not a c# or another application), as a workaroud; use a temp or persistent table to store replacement options.
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL
DROP TABLE #tmp
CREATE TABLE #tmp (
fromText VARCHAR(16),
toText VARCHAR(16)
);
INSERT INTO #tmp (fromText, toText)
VALUES
('POLYESTER', 'POLY'),
('COTTON', 'CTN'),
('GRAPHIC', 'GRPHC')
DECLARE #someValue AS NVARCHAR(MAX) =
'**Some string value **COTTON **Some string value ** POLYESTER';
SELECT #someValue = REPLACE(#someValue, fromText, toText) FROM #tmp;
PRINT #someValue
and the result is:
**Some string value **CTN **Some string value ** POLY.
The answer of mehmetx is actually very nice.
If you need your replacement functionality on a regular basis, you could think about using a normal table instead of a temporary table.
But if you need this logic only once in a while, and performance is not much of an issue, you could avoid the additional replacements table altogether and use a table expression in the FROM clause instead. Something like this:
DECLARE #someValue AS NVARCHAR(MAX) = '**Some string value **COTTON **Some string value ** POLYESTER';
SELECT #someValue = REPLACE(#someValue, fromText, toText)
FROM
(VALUES
('POLYESTER', 'POLY'),
('COTTON', 'CTN'),
('GRAPHIC', 'GRPHC')
) AS S (fromText, toText);
EDIT:
I noticed, that this logic regrettably does not work as expected when used in an UPDATE statement to update existing data in a table.
For that purpose (if needed), I created a user-defined function that performs the replacement logic. I called it MultiReplace. And it does not use the replacement data from a temporary table, but from a "normal" table, which I called Replacements.
The following code demonstrates it. It uses a data table called MyData, which gets updated with all replacements in the Replacements table using the MultiReplace function:
IF OBJECT_ID('MultiReplace') IS NOT NULL
DROP FUNCTION MultiReplace;
IF OBJECT_ID('Replacements') IS NOT NULL
DROP TABLE Replacements;
IF OBJECT_ID('MyData') IS NOT NULL
DROP TABLE MyData;
GO
CREATE TABLE Replacements (
fromText VARCHAR(100),
toText VARCHAR(100)
);
CREATE TABLE MyData (
SomeValue VARCHAR(MAX)
)
GO
CREATE FUNCTION MultiReplace(#someValue AS VARCHAR(MAX))
RETURNS VARCHAR(MAX)
AS
BEGIN
SELECT #someValue = REPLACE(#someValue, fromText, toText) FROM Replacements;
RETURN #someValue;
END;
GO
INSERT INTO MyData (SomeValue)
VALUES
('**Some string value **COTTON **Some string value ** POLYESTER');
INSERT INTO Replacements (fromText, toText)
VALUES
('POLYESTER', 'POLY'),
('COTTON', 'CTN'),
('GRAPHIC', 'GRPHC');
SELECT * FROM MyData;
UPDATE MyData SET SomeValue = dbo.MultiReplace(SomeValue)
SELECT * FROM MyData;
But perhaps using multiple REPLACE statements might be more straightforward after all?...
EDIT 2:
Based on the short conversation in the comments, I could propose a simpler solution that uses multiple REPLACE statements in a clearer way. I have only tested it on SQL Server 2019; I am not sure if it will work correctly on SQL Server 2012.
Again, I use a table called MyData for testing here. But there are no additional database objects anymore.
Regrettably, I did not get it to work with a temporary table containing the replacement values.
-- Preparations:
IF OBJECT_ID('MyData') IS NOT NULL
DROP TABLE MyData;
CREATE TABLE MyData (
SomeValue VARCHAR(MAX)
);
INSERT INTO MyData
VALUES
('**Some string value **COTTON **Some string value ** POLYESTER'),
('**Another string value **GRAPHIC **Another string value ** POLYESTER');
-- Actual work:
SELECT * FROM MyData; -- Show the state before updating
DECLARE #someValue VARCHAR(MAX);
UPDATE MyData
SET
#someValue = SomeValue,
#someValue = REPLACE(#someValue, 'POLYESTER', 'POLY'),
#someValue = REPLACE(#someValue, 'COTTON', 'CTN'),
#someValue = REPLACE(#someValue, 'GRAPHIC', 'GRPHC'),
SomeValue = #someValue;
SELECT * FROM MyData; -- Show the state after updating

How do I create a variable/parameter that is a string of values in SQL SSMS that I can use as a substitute in my where clause?

This may be a very basic question, but I have been struggling with this.
I have a SSMS query that I'll be using multiple times for a large set of client Ids. Its quite cumbersome to have to amend the parameters in all the where clauses every time I want to run it.
For simplicity, I want to convert a query like the one below:
SELECT
ID,
Description
From TestDb
Where ID in ('1-234908','1-345678','1-12345')
to a query of the format below so that I only need to change my variable field once and it can be applied across my query:
USE TestDb
DECLARE #ixns NVARCHAR(100)
SET #ixns = '''1-234908'',''1-345678'',''1-12345'''
SELECT
ID,
Description
From TestDb
Where ID IN #ixns
However, the above format doesn't work. Can anyone help me on how I can use a varchar/string variable in my "where" clause for my query so that I can query multiple IDs at the same time and only have to adjust/set my variable once?
Thanks in advance :D
The most appropriate solution would be to use a table variable:
DECLARE #ixns TABLE (id NVARCHAR(100));
INSERT INTO #ixns(id) VALUES
('1-234908'),
('1-345678'),
('1-12345');
SELECT ID, Description
FROM TestDb
WHERE ID IN (SELECT id FROM #ixns);
You can load ids to temp table use that in where condition
USE TestDb
DECLARE #tmpIDs TABLE
(
id VARCHAR(50)
)
insert into #tmpIDs values ('1-234908')
insert into #tmpIDs values ('1-345678')
insert into #tmpIDs values ('1-12345')
SELECT
ID,
Description
From TestDb
Where ID IN (select id from #tmpIDs)
The most appropriate way is to create a table type because it is possible to pass this type as parameters.
1) Creating the table type with the ID column.
create type MyListID as table
(
Id int not null
)
go
2) Creating the procedure that receives this type as a parameter.
create procedure MyProcedure
(
#MyListID as MyListID readonly
)
as
select
column1,
column2
...
from
MyTable
where
Id in (select Id from #MyListID)
3) In this example you can see how to fill this type through your application ..: https://stackoverflow.com/a/25871046/8286724

is it possible to "clone" a table variable?

I have a table variable with about 20 columns. I'd like to essentially reuse a single table variable structure for 2 different result sets. The 2 result sets should be represented in different table variables so I can't reuse a single table variable. Therefore, I was wondering if there was a way to clone a single table variable for reuse. For example, something like this:
DECLARE #MyTableVar1 TABLE(
Col1 INT,
Col2 INT
}
DECLARE #MyTableVar2 TABLE = #MyTableVar1
I'd like to avoid creating duplicate SQL if I can reuse existing SQL.
That is not possible, use temp table instead
if object_id('tempdb..#MyTempTable1') is not null drop table #MyTempTable1
Create TABLE #MyTempTable1 (
Col1 INT,
Col2 INT
)
if object_id('tempdb..#MyTempTable2') is not null drop table #MyTempTable2
select * into #MyTempTable2 from #MyTempTable1
update :
As suggested by Eric in comment, if you are looking for just table schema and not the data inside the first table then
select * into #MyTempTable2 from #MyTempTable1 where 1 = 0
You can create a user-defined table type which is typically meant for using table valued parameters for stored procedures. Once the type is created, you can use it as a type to declare any number of table variables just like built-in types. This comes closest to you requirement.
Ex:
CREATE TYPE MyTableType AS TABLE
( COL1 int
, COL2 int )
DECLARE #MyTableVar1 AS MyTableType
DECLARE #MyTableVar2 AS MyTableType
A few things to note with this solution
MyTableType becomes a database level type. It is not local to a specific stored procedure.
If you have to ever change the definition of the table, then you have to drop the code/sprocs using the TVP type, then recreate the table type with new definition and related sprocs. Typically this is a non-issue as the code and the type are created/recreated together.
You could use a temp table and select into... they perform better since their statistics are better.
create table #myTable(
Col1 INT null,
Col2 INT null
}
...
select *
into #myTableTwo
from #myTable
You can create one table variable and add type column in the table and use the type column in your queries to filter the data.
By this you are using one table to hold more than one type of data.
Hope this helps.
declare #myTable table(
Col1 INT null,
Col2 INT null,
....
Type INT NULL
}
insert into #myTable(...,type)
select ......,1
insert into #myTable(...,type)
select ......,2
select * from #myTable where type =1
select * from #myTable where type =2

INSERT SELECT in Firebird

I'm new to firebird and I have verious issues. I want to insert various lines into a table selected from another table.
Here's the code:
/*CREATE GENERATOR POS; */
SET GENERATOR POS TO 1;
SET TERM ^;
create trigger BAS_pkassign
for MATERIAL
active before insert position 66
EXECUTE BLOCK
AS
declare posid bigint;
select gen_id(POS, 1)
from RDB$DATABASE
into :posid;
BEGIN
END
SET TERM ; ^
INSERT INTO MATERIAL ( /*ID */ LOCATION, POSID, ARTID, ARTIDCONT, QUANTITY )
SELECT 1000, ':posid', 309, BAS_ART.ID, 1
FROM BAS_ART
WHERE BAS_ART.ARTCATEGORY LIKE '%MyWord%'
The ID should autoincrement from 66 on. The posid should autoincrement from 1 on.
Actually it is not inserting anything.
I'm using Firebird Maestro and have just opened the SQL Script Editor (which doesnt throw any error message on executing the script).
Can anybody help me?
Thanks!
Additional information:
The trigger should autoincrement the column "ID" - but I dont know how exactly I can change it so it works.. The ':posid' throws an error using it :posid but like this theres no error (I guess its interpretated as a string). But how do I use it right?
I dont get errors when I execute it. The table structure is easy. I have 2 tables:
1.
Material (
ID (INTEGER),
Location (INTEGER),
POSID (INTEGER),
ARTID (INTEGER),
ARTIDCONT (INTEGER),
QUANTITY (INTEGER),
OTHERCOLUMN (INTEGER))
and the 2. other table
BAS_ART (ID (INTEGER), ARTCATEGORY (VARCHAR255))
-> I want to insert all entries from the table BAS_ART which contain "MyWord" in the column ARTCATEGORY into the MATERIAL table.
I don't understand why you need the trigger at all.
This problem:
I want to insert all entries from the table BAS_ART which contain "MyWord" into the MATERIAL table
Can be solved with a single insert ... select statement.
insert into material (id, location, posid, artid, quantity)
select next value for seq_mat_id, 1000, next value for seq_pos, id, 1
from bas_art
where artcategory = 'My Word';
This assumes that there is a second sequence (aka "generator") that is named seq_mat_id that provides the new id for the column material.id
For most of my answer I will assume a very simple table:
CREATE TABLE MyTable (
ID BIGINT PRIMARY KEY,
SomeValue VARCHAR(255),
posid INTEGER
)
Auto-increment identifier
Firebird (up to version 2.5) does not have an identity column type (this will be added in Firebird 3), instead you need to use a sequence (aka generator) and a trigger to get this.
Sequence
First you need to create a sequence using CREATE SEQUENCE:
CREATE SEQUENCE seqMyTable
A sequence is atomic which means interleaving transactions/connections will not get duplicate values, it is also outside transaction control, which means that a ROLLBACK will not revert to the previous value. In most uses a sequences should always increase, so the value reset you do at the start of your question is wrong for almost all purposes; for example another connection could reset the sequence as well midway in your execution leaving you with unintended duplicates of POSID.
Trigger
To generate a value for an auto-increment identifier, you need to use a BEFORE INSERT TRIGGER that assigns a generated value to the - in this example - ID column.
CREATE TRIGGER trgMyTableAutoIncrement FOR MyTable
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
NEW.ID = NEXT VALUE FOR seqMyTable;
END
In this example I always assign a generated value, other examples assign a generated value only when the ID is NULL.
Getting the value
To get the generated value you can use the RETURNING-clause of the INSERT-statement:
INSERT INTO MyTable (SomeValue) VALUES ('abc') RETURNING ID
INSERT INTO ... SELECT
Using INSERT INTO ... SELECT you can select rows from one table and insert them into others. The reason it doesn't work for you is because you are trying to assign the string value ':pos' to a column of type INTEGER, and that is not allowed.
Assuming I have another table MyOtherTable with a similar structure as MyTable I can transfer values using:
INSERT INTO MyTable (SomeValue)
SELECT SomeOtherValue
FROM MyOtherTable
Using INSERT INTO ... SELECT it is not possible to obtain the generated values unless only a single row was inserted.
Guesswork with regard to POSID
It is not clear to me what POSID is supposed to be, and what values it should have. It looks like you want to have an increasing value starting at 1 for a single INSERT INTO ... SELECT. In versions of Firebird up to 2.5 that is not possible in this way (in Firebird 3 you would be able to use ROW_NUMBER() for this).
If my guess is right, then you will need to use an EXECUTE BLOCK (or a stored procedure) to assign and increase the value for every row to be inserted.
The execute block would be something like:
EXECUTE BLOCK
AS
DECLARE posid INTEGER = 1;
DECLARE someothervalue VARCHAR(255);
BEGIN
FOR SELECT SomeOtherValue FROM MyOtherTable INTO :someothervalue DO
BEGIN
INSERT INTO MyTable (SomeValue, posid) VALUES (:someothervalue, :posid);
posid = posid + 1;
END
END
Without an ORDER BY with the SELECT the value of posid is essentially meaningless, because there is no guaranteed order.