Dynamically update SQLite row based on column values - sqlite

Is it possible for SQLite to dynamically insert/update values in the row of a table based on other columns within that row? Best illustrated with a simple example
CREATE TABLE family(first TEXT, last TEXT, full TEXT *magical specification);
INSERT INTO family (first,last) VALUES ('foo','bar')
And have the table automatically fill in the third column with a combination of the other two:
first last full
--------- ------------ -----------
foo bar foo bar

You could use a trigger:
CREATE TRIGGER family_full
AFTER INSERT ON family
FOR EACH ROW
WHEN NEW.full IS NULL
BEGIN
UPDATE family
SET full = first || ' ' || last
WHERE rowid = NEW.rowid;
END;

Related

How to solve this issue in pl/sql

How to create a trigger which interdicts insertion of symbols and space in a certain column and after insertion just to have only the upper letters
for example:
insert into tale xxx values '"&$))(/-$:&##¥*|^]asjdj';
and the result should be the following:
ASJDJ
thank you
a lot of functions procedures trigger and nothing was right
Add a new column to the table and update that column extracting only the letters from the column with special characters.
Using your eg.
Add new column value in tale table. Right after inserting the xxx column do the updates like:
update tale set value = upper(regexp_substr(xxx,'[[:alpha:]]+'));
That would be a row-level trigger which fires both before insert or update on table so that value is modified in any case. There are various options which let you remove everything but letters; regular expression is simple enough.
Sample table:
SQL> create table test (col varchar2(40));
Table created.
Trigger:
SQL> create or replace trigger trg_biu_test
2 before insert or update on test
3 for each row
4 begin
5 :new.col := upper(regexp_replace(:new.col, '[^[:alpha:]]'));
6 end;
7 /
Trigger created.
Testing:
SQL> insert into test (col) values ('"&$))(/-$:&##¥*|^]asjdj');
1 row created.
SQL> select * from test;
COL
----------------------------------------
YASJDJ
SQL> update test set col = '25xyz';
1 row updated.
SQL> select * from test;
COL
----------------------------------------
XYZ
SQL>

Insert Into Select Case Statement

I am using an Insert Into Select to move a new table into a master table. I need to have either one of two columns [customMessage1] or [customMessage2] in the new table be entered into the master table. The good data in the new table starts 'SO' in either column. [customMessage2] only has a good value if [customMessage1] does not in the imported data.
INSERT INTO [Freight].[dbo].[MASTER]
Select
[transactionType]
,[amount]
,[transactionId]
,CASE
WHEN [customMessage1] LIKE 'SO%'
THEN [customMessage1]
ELSE
[customMessage2]
END
FROM [Freight].[dbo].[NEW]

SQLite check for duplicate rows

I have an SQLite database for an art exhibition. In the table "exhibits" I have columns for the artwork ID, the exhibition space ID, a begin date, and an end date. The default value for "end date" is NULL.
Of course, the same artwork cannot be displayed in two different spaces at once. So I want to ensure that a new row with an artwork ID is not created unless all existing rows with that same artwork ID have a non-null end date.
Is there some kind of constraint, trigger, etc. that I can add to the table to ensure this?
I am not an expert on writing triggers for SQLite but something like this should work,
CREATE TRIGGER check_open_ended_exhibit BEFORE INSERT ON exhibits
BEGIN
SELECT RAISE(ABORT, "Open ended exhibit exists")
WHERE EXISTS(SELECT * FROM exhibits WHERE artworkID = NEW.artworkID AND enddate IS NULL);
END
According to your information “Artwork” cannot be displayed twice in the same show which means the EndTime is a unique field when constraining it together with Artwork. So by making these two together your constrain you won’t be able to insert a record if you already have “artwork and NULL”.
So yeah you can just create a unique constrain on these two columns.
CREATE TABLE testConstrain (
id INTEGER NOT NULL,
endDate DATETIME
)
CREATE UNIQUE INDEX testConstrain
ON testConstrain(id, endDate);
INSERT INTO testConstrain VALUES('1',null)
INSERT INTO testConstrain VALUES('2','01-01-2018')
INSERT INTO testConstrain VALUES('1','01-01-2018')
INSERT INTO testConstrain VALUES('1',null)
`
And you will get:
Started executing query at Line 11
(1 row affected)
(1 row affected)
(1 row affected)
Msg 2601, Level 14, State 1, Line 4
Cannot insert duplicate key row in object 'bginsburg.testConstrain' with unique index 'testConstrain'. The duplicate key value is (1, ).
The statement has been terminated.

how to select column based on its on order in Table

I want to select Column from Table based on Its Order
like
create Table Products
(
ProductId Int,
ProductName varchar(50)
)
lets Say I don't Know the name of the second column.
How I can get it like :
Select Col1,Col2 From Product
For SQL Server:
You can't do this in the SELECT clause. You can't select based on the order number of the column. You have to list the columns' names you need to select explicitly, otherwise, use SELECT * to list all. Me be if you are using a data reader object or any other ado.net methods to get the data from database you can do something like this, but this will be based on the column names list listed in your SQL statement.
However, you can do something like this dynamically, by reading columns' metadata ordinal_position from information_schema.columns as explained in the following answer:
Is it possible to select sql server data using column ordinal position?
But, you can do this in the ORDER BY clause. You can ORDER BY column number:
SELECT *
FROM TableName
ORDER BY 2; -- for col2
But this is not recommended to use in ORDER BY or in the SELECT (if any). Furthermore, columns order is not significant in the relational model.
Update: If you want to select at least 3 columns from any table parameter passed to your stored procedure. Try this as follows:
Your stored procedure supposed to receive a parameter #tableNameParam. The folowing code should return the first three columns from the #tablenameParam passed to the stored procedure:
DECLARE #col1 AS VARCHAR(100);
DECLARE #col2 AS VARCHAR(100);
DECLARE #col3 AS VARCHAR(100);
DECLARE #tableNameParam AS VARCHAR(50) = 'Tablename';
DECLARE #sql AS VARCHAR(MAX) ;
SELECT #col1 = column_name FROM information_schema.columns
WHERE table_name = #tableNameParam
AND ordinal_position = 1;
SELECT #col2 = column_name FROM information_schema.columns
WHERE table_name = #tableNameParam;
AND ordinal_position = 2;
SELECT #col3 = column_name FROM information_schema.columns
WHERE table_name = #tableNameParam;
AND ordinal_position = 3;
SET #sql = 'SELECT ' + col1 + ',' + col2 ' + 'col3 ' + FROM ' + #tablename;
you always can do
select * from Product
I'd like to share the following code as a solution to CRUD processing on Ordinal Position within a table. I had this problem today and it took me quite a long time to research and find a working solution. Many of the posted answers indicated that it was not possible to interact with the tables columns on an Ordinal bases but as indicated in the post above using the information_schema table will allow using the column position.
My situation was interacting with a table populated through the use of a pivot view so the columns are always changing based on the data, which is fine in a view result but when the dataset is stored into a table the columns are dynamic. The column names are a Year-Month combination such as 201801, 201802 with an Item Number as a primary key. This pivot table is to indicate manufacturing quantities by Year-Month on a rolling 12 month period so each month the column names with change/shift which changes their ordinal position when the table is rebuilt each month.
The Pivot view is used to build the Staging table, The Staging table is used to build the
Target table so the ordinal position of the staging and target tables are lined up with the same ordinal position.
Declare #colname Varchar(55) -- Column Name
Declare #ordpos INT -- Ordinal Position
Declare #Item Varchar(99) -- PK
Declare #i INT -- Counter
Declare #cnt INT -- Count
Declare #ids table(idx int identity(1,1), Item Varchar(25))
-- Item List
Insert INTO #ids Select Item From DBName.Schema.TableName
select #i = min(idx) - 1, #cnt = max(idx) from #ids
-- Row Loop
While #i < #cnt
Begin
Select #i = #i + 1
Set #ordpos=3
Set #Item = (select Item from #ids where idx = #i)
-- Column Loop
While #ordpos < 27
Begin
Select #colname =column_name From INFORMATION_SCHEMA.Columns Where table_name='TargetTable' and ordinal_position=#ordpos
Exec ('Update TargetTable set ['+#colname+']= (Select ['+#colname+'] From StagingTable Where Item='''+#Item+''') where Item='''+#Item+'''')
Set #ordpos=#ordpos + 1
End -- End Column Loop
End -- End Row Loop
The code here will loop through the Item matrix by rows and by columns and uses Dynamic SQL to build the action, in this case the action is an update but it could just as easily be a select. Each column is processed through the While Loop and then loops through the next row. This allows updates to a specific cell in the matrix by (Item X YearMonth) without actually knowing what the column name at a given position.
The one concern is that depending on the size of the data in this matrix it can be SLOW. I just wanted to show this as a way to use unknown column names in an ordinal position.

How to generate columns dynamically?

I have table with certain number of columns.
I want to populate other table with the data of a particular column of Table1 as columns of table2 dynamically.
When I say dynamically I mean to say that when ever any data is added to the column of Table1 the table2 is populated with as many number of columns.
Changing the schema on the fly really isn't a good idea, for a number of reasons. From what you've described, I think you would be better off using a view for this. A view will give you the dynamic capabilities you're looking for with fewer side effects.
See this article:
How to create a view in SQL Server
I will once again repeat the disclaimer that this is a bad idea, many things can go wrong, and I'm certain there is a better solution to whatever underlying problem you're trying to solve. That said, to answer the explicit question anyway, here is an example of how to do this:
USE tempdb;
GO
CREATE TABLE dbo.Table1(Description VARCHAR(32));
CREATE TABLE dbo.Table2(ID INT);
GO
CREATE TRIGGER dbo.CatchNewTable1Data
ON dbo.Table1
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += CHAR(13) + CHAR(10) +
'ALTER TABLE dbo.Table2 ADD '
+ QUOTENAME(d) + ' VARCHAR(255);' -- guessing on destination data type
FROM
(
SELECT DISTINCT d = LEFT([Description], 128) -- identifier <= 128
FROM inserted AS i
WHERE NOT EXISTS
(
SELECT 1 FROM sys.columns
WHERE name = LEFT(i.[Description], 128)
AND [object_id] = OBJECT_ID('dbo.Table2')
)
) AS x;
EXEC sp_executesql #sql;
END
GO
Now, let's try it out! Try a column that already exists, a multi-row insert where one of the columns already exists, a multi-row insert with dupes, etc. I am not posting a value > 255 nor am I dealing with any fancy characters that will cause a problem. Why? Because ultimately I don't want you to use this solution, I want to solve the real problem. But for the googlers I want to show that there is a solution to the stated problem.
-- does nothing:
INSERT dbo.Table1 SELECT 'ID';
-- only adds column 'foo':
INSERT dbo.Table1 SELECT 'ID'
UNION ALL SELECT 'foo';
-- adds both of these columns:
INSERT dbo.Table1 SELECT 'bar'
UNION ALL SELECT 'splan foob';
-- only adds one of these:
INSERT dbo.Table1 SELECT 'blat'
UNION ALL SELECT 'blat';
SELECT * FROM dbo.Table2;
Results:
ID foo bar splan foob blat
----------- ------------ ------------ ------------ ------------
Don't forget to clean up:
DROP TABLE dbo.Table1, dbo.Table2;

Resources