Add calculated column in Snowfalke - sql

Again, I can't seem to find a simple way to do this.
But I've been coding for a while now and potentially I'm just tired and losing it!
SELECT
case when "colA" = '1' OR "colB" = 1 then 1
else 0
end as "colc"
FROM t1
How do I add this new column to t1, preserving the order etc.?
I know I can run
ALTER t1 ADD COLUMN colc
but how do I populate the values correctly?

Despite the documentation you can find around, it is actually possible to create computed columns. Here is an example:
create or replace table computedColTest (id bigint, id2 bigint, derived bigint as (id * id2));
insert into computedColTest values (3, 5);
select * from computedColTest;
The result is:
However, I don't think it is possible to directly translate the logic you need as you can't seem to be able to use a logical operator within it. You can however adapt it a bit to your situation and translate your switch into a mathematical operation that is compatible.

I think 'Virtual Columns' is how derived columns are known in Snowflake.
Check this out:
CREATE
OR REPLACE TABLE Virtual_Column_Example (
colA INT
, colB INT
, derived INT AS ( IFF(colA = 1 or colB = 1, 1, 0) )
);
INSERT INTO Virtual_Column_Example
VALUES (0, 0), (1, 1), (1, 2), (2, 1),(2, 2);
SELECT * FROM Virtual_Column_Example;
/*
---------------------
colA | colB | derived
---------------------
0 | 0 | 0
1 | 1 | 1
1 | 2 | 1
2 | 1 | 1
2 | 2 | 0
---------------------
*/

I actually found a way that works well, just use UPDATE
ALTER TABLE t1 ADD COLUMN "col3" INTEGER DEFAULT 0;
UPDATE t1
SET "col3"= 1
WHERE "col1" = 1 OR "col2" = 1;

Related

SQL Server Add a Bit Column with 2 default values, and it should default to one value

I have to add a new column Isactive with (data type bit, not null)
ANd Populate the column with:
1 = active
0 = not active (default value).
Thanks in Advance.
I tried this by searching old question :
"
alter table tblname
add (STATUS VARCHAR(15) default '0',
constraint conSTATUS check (STATUS in ('1', '0')))
"
but how to set 1 = active & 0 = not active.
Add a computed column. This is not a constraint thing:
alter table NCT_UserRegistration
add isactive as (case when status = 'Active' then 1 else 0 end);
bit is not really a SQL data type (although some databases do support it. An int should be fine for your purposes.
but how to set 1 = active & 0 = not active
You don't set anything.
STATUS is a flag column and it can be translated in the presentation layer as you wish.
When you query the table use a CASE expression like this:
select
case STATUS
when 0 then 'not active'
when 1 then 'active'
end
from tblname
I believe you wanted to know how will you add values true and false to the table. I will start from a demo sample of your table(just for example):
create table tblname(id int, name varchar(20))
I will insert one row:
insert into tblname values (1, 'Marc')
This select query select * from tblname will result:
| ID | name |
| 1 | Marc |
Then we alter the table and add a BIT column called Isactive (with the constraint from your question)
alter table tblname add
Isactive bit default 0 not null,
constraint conSTATUS check (Isactive in ('1', '0'));
Because the default value is 0 then the row with ID = 1 and name = 'Marc' will have a new value of the column Isactive set to 0. But when you run this query select * from tblname again it will give you this results:
| ID | name | isactive |
| 1 | Marc | False |
If you then insert another row in your table like this:
insert into tblname values (2, 'Ann', 1);
with this query select * from tblname your results will be:
| ID | name | isactive |
| 1 | Marc | False |
| 1 | Ann | True |
I hope this helps.
Here is a DEMO
P.S. More interesting thins about BIT type in SQLServer you can find here:
https://www.sqlservertutorial.net/sql-server-basics/sql-server-bit/

Insert different values per column at once

After adding a new column is it possible to insert values for that column using only one query?
TABLE:
id | category | level | new_column_name
---+-------------+-------+----------------
1 | Character | 3 |
2 | Character | 2 |
3 | Character | 2 |
4 | Character | 5 |
I'd need to do something like
INSERT INTO table_name
(new_column_name)
VALUES
('foo'), -- value for new_column_name : row1
('bar'), -- value for new_column_name : row2
...
;
I already used a similar query using postgresql cli in order to insert values, but it fails becauase uses null for the unset column values, one of which (id) is PRIMARY_KEY, so it can't be NULL.
This is the error it logs:
ERROR: null value in column "id" violates not-null constraint
DETAIL: Failing row contains (null, null, null, 1359).
EDIT:
What I'm doing is more an update than an insert, but what I would be able to do is to insert all the different values at once.
For instance, if possible, I would avoid doing this:
UPDATE table_name
SET new_column_name = 'foo'
WHERE id = 1;
UPDATE table_name
SET new_column_name = 'bar',
WHERE id = 2;
--...
You can use VALUES so as to construct an in-line table containing the values to be updated.:
UPDATE table_name AS v
SET new_column_name = s.val
FROM (VALUES (1, 'foo'), (2, 'bar')) AS s(id, val)
WHERE v.id = s.id
Demo here
You can use a huge CASE:
UPDATE table_name
SET new_column_name
= CASE WHEN id = 1 THEN 'foo'
WHEN id = 2 THEN 'bar'
END;

Alter Table, Add Column and Set column value in one query?

Is it possible to, in the same query, add a column to an existing table and set that column's value using case when?
ALTER TABLE #totalrevenue
ADD TotalRevenue_2003 Int CASE WHEN (Full2003 + Half2003 = 0) THEN NULL ELSE 1 END
FROM data.revenuesummary
WHERE #totalrevenue.ID = data.revenuesummary.ID'
Thanks!
What about using Computed columns?
Read more here:
https://msdn.microsoft.com/en-us/library/ms188300.aspx
Tiny example
CREATE TABLE dbo.#Products
(
ID int IDENTITY (1,1) NOT NULL
, Full2003 smallint
, Half2003 smallint
, TotalRevenue2003 AS IIF(Full2003 + Half2003 = 0, NULL, 1)
);
INSERT INTO dbo.#Products
(Full2003, Half2003)
VALUES
(2, 3),
(2, 2),
(2, -2)
SELECT *
FROM dbo.#Products
Results in:
ID Full2003 Half2003 TotalRevenue2003
1 2 3 1
2 2 2 1
3 2 -2 NULL

SQL Sever 2008 R2 - Transforming a table with xml list columns to individual rows in new table

I'm trying to write some SQL to help transition from one database to another. It's gone well so far, but I ran into a problem I can't wrap my brain around.
Original:
Id (bigint) | ColA (XML) | ColB (XML) | ... | RecordCreation
The XML for each column with XML looks like the following:
<ColA count="3"><int>3</int><int>9</int><int>6</int></ColA>
For any particular row, the "count" is the same for each list, ColB will also have 3, etc., but some lists are of strings.
In the new database:
Id (bigint) | Index (int) | ColA (int) | ColB (nvarchar(20)) | ... | RecordCreation
So if I start with
5 | <ColA count="3"><int>9</int><int>8</int><int>7</int></ColA> | <ColB count="3"><string>A</string><string>B</string><string>C</string></ColB> | ... | 2014-01-15 ...
I need out:
5 | 1 | 9 | A | ... | 2014-01-15 ...
5 | 2 | 8 | B | ... | 2014-01-15 ...
5 | 3 | 7 | C | ... | 2014-01-15 ...
For each of the rows in the original DB where Index (second column) is the position in the XML list the values for that row are coming from.
Any ideas?
Thanks.
Edit:
A colleague showed me a dirty way that looks like it might get me there. This is to transfer some existing data into the new database for testing purposes, it's not production and won't be used often; we're just starving for data to test on.
declare #count int
set #count = 0
declare #T1 ( Id bigint, Index int, ColA int, ColB nvarchar(20),..., MaxIndex int)
while #count < 12 begin
Insert into #T1
select Id, #count,
CAST(CONVERT(nvarchar(max), ColA.query('/ColA/int[sql:variable("#count")]/text()')) as int),
CONVERT(nvarchar(20), ColB.query('/ColB/string[sql:variable("#count")]/text()')),
...,
CAST(CONVERT(nvarchar(max), ColA.query('data(/ColA/#count)')) as int)
From mytable
set #count = #count + 1
end
Then I can insert from the temp table where Index < MaxIndex. There'll never be more than 12 indices and I think index is 0 based; easy fix if not. And each row may have a different count in its lists (but all lists of the same row will have the same count); that's why I went with MaxIndex and a temp table. And I may switch to real table that I drop when I'm done if the performance is too bad.
Try this query:
DECLARE #MyTable TABLE (
ID INT PRIMARY KEY,
ColA XML,
ColB XML
);
INSERT #MyTable (ID, ColA, ColB)
SELECT 5, N'<ColA count="3"><int>9</int><int>8</int><int>7</int></ColA>', N'<ColB count="3"><string>A</string><string>B</string><string>C</string></ColB>';
SELECT x.ID,
ab.*
FROM #MyTable x
CROSS APPLY (
SELECT a.IntValue, b.VarcharValue
FROM
(
SELECT ax.XmlCol.value('(text())[1]', 'INT') AS IntValue,
ROW_NUMBER() OVER(ORDER BY ax.XmlCol) AS RowNum
FROM x.ColA.nodes('/ColA/int') ax(XmlCol)
) a INNER JOIN
(
SELECT bx.XmlCol.value('(text())[1]', 'VARCHAR(50)') AS VarcharValue,
ROW_NUMBER() OVER(ORDER BY bx.XmlCol) AS RowNum
FROM x.ColB.nodes('/ColB/string') bx(XmlCol)
) b ON a.RowNum = b.RowNum
) ab;
Output:
/*
ID IntValue VarcharValue
-- -------- ------------
5 9 A
5 8 B
5 7 C
*/
Note: very likely, the performance could be horrible (even for an ad-hoc task)
Assumption:
For any particular row, the "count" is the same for each list, ColB
will also have 3, etc., but some lists are of strings.
A colleague showed me a dirty way that looks like it might get me there. This is to transfer some existing data into the new database for testing purposes, it's not production and won't be used often; we're just starving for data to test on.
declare #count int
set #count = 0
declare #T1 ( Id bigint, Index int, ColA int, ColB nvarchar(20),..., MaxIndex int)
while #count < 12 begin
Insert into #T1
select Id, #count,
CAST(CONVERT(nvarchar(max), ColA.query('/ColA/int[sql:variable("#count")]/text()')) as int),
CONVERT(nvarchar(20), ColB.query('/ColB/string[sql:variable("#count")]/text()')),
...,
CAST(CONVERT(nvarchar(max), ColA.query('data(/ColA/#count)')) as int)
From mytable
set #count = #count + 1
end
Then I can insert from the temp table where Index < MaxIndex. There'll never be more than 12 indices and I think index is 0 based; easy fix if not. And each row may have a different count in its lists (but all lists of the same row will have the same count); that's why I went with MaxIndex and a temp table. And I may switch to real table that I drop when I'm done if the performance is too bad.

In a persisted field, how do you return the number of occurrences of a column within a different table's column

The following is required due to records being entered by 3rd parties in a web application.
Certain columns (such as Category) require validation including the one below. I have a table OtherTable with the allowed values.
I need to identify how many occurrences (ie: IF) there are of the current table's column's value in a different table's specified column. If there are no occurrences this results in a flagged error '1', if there are occurrences, then it results in no flagged error '0'.
If `Category` can be found in `OtherTable.ColumnA` then return 0 else 1
How can I do this please?
If Category can be found in OtherTable.ColumnA then return 0 else 1
You could use CASE with EXISTS
SELECT CASE WHEN EXISTS(
SELECT NULL
FROM AllowedValues av
WHERE av.ColumnA = Category
) THEN 0 ELSE 1 END AS ErrorCode
, Category
FROM [Table]
Edit: Here's a sql-fiddle: http://sqlfiddle.com/#!3/55a2e/1
Edit: I've only just noticed that you want to use a computed column. As i've read you can only use it with scalar values and not with sub-queries. But you can create a scalar valued function.
For example:
create table AllowedValues(ColumnA varchar(1));
insert into AllowedValues Values('A');
insert into AllowedValues Values('B');
insert into AllowedValues Values('C');
create table [Table](Category varchar(1));
insert into [Table] Values('A');
insert into [Table] Values('B');
insert into [Table] Values('C');
insert into [Table] Values('D');
insert into [Table] Values('E');
-- create a scalar valued function to return your error-code
CREATE FUNCTION udf_Category_ErrorCode
(
#category VARCHAR(1)
)
RETURNS INT
AS BEGIN
DECLARE #retValue INT
SELECT #retValue =
CASE WHEN EXISTS(
SELECT NULL
FROM AllowedValues av
WHERE av.ColumnA = #category
) THEN 0 ELSE 1 END
RETURN #retValue
END
GO
Now you can add the column as computed column which uses the function to calculate the value:
ALTER TABLE [Table] ADD ErrorCode AS ( dbo.udf_Category_ErrorCode(Category) )
GO
Here's the running SQL: http://sqlfiddle.com/#!3/fc49e/2
Note: as #Damien_The_Unbelieve has commented at the other answer, even if you persist the result with a UDF, the value won't be updated if the rows in OtherTable change. Just keep that in mind, so you need to update the table manually if desired with the help of the UDF.
select mt.*,IFNULL(cat_count.ct,0) as Occurrences from MainTable mt
left outer join (select ColumnA,count(*) as ct from OtherTable) cat_count
on mt.Category=cat_count.ColumnA
Result:
mt.col1 | mt.col2 | Category | Occurrences
### | ### | XXX | 3
### | ### | YYY | 0
### | ### | ZZZ | 1