function upsert inserting null values - database

I created the following function upsert(insert or edit if it's already exist) in postgresql using pgadmin 4.
CREATE OR REPLACE FUNCTION upsert(
uname character varying(55),
fname character varying(55),
eml character varying(255),
psw character varying(265),
phonenbr character varying(55),
adrs character varying(300)
)
RETURNS table (j json) AS
$$
BEGIN
INSERT INTO users
VALUES (DEFAULT,uname, fname, eml, psw, phonenbr, adrs)
ON CONFLICT (username, firstname)
DO
UPDATE SET username = EXCLUDED.username, firstname = EXCLUDED.firstname,
email = EXCLUDED.email, password = EXCLUDED.password, phonenumber = EXCLUDED.phonenumber,
address = EXCLUDED.address, registrationdate=current_timestamp, subscriptionend =current_timestamp+ INTERVAL '1 month',stat='active';
END
$$
LANGUAGE 'plpgsql';
The issue is than when it's doing an insert, for 3 column the values are null but everything it's fine when it's doing an update.
He there a way to solved this issue without adding 3 more parameters to the function ?
the scrip for create the table:
CREATE TABLE users
(
id_user integer Generated Always as Identity,
username character varying(55) NOT NULL,
firstname character varying(55) NOT NULL,
email character varying(255) NOT NULL,
password character varying(255)NOT NULL,
phonenumber character varying(55)NOT NULL,
address character varying(300) NOT NULL,
subscriptionend timestamp without time zone ,
registrationdate timestamp without time zone,
stat status,
CONSTRAINT users_pkey PRIMARY KEY (username,firstname)
)

I find a solution by declaring and using variables inside the function:
CREATE OR REPLACE FUNCTION upsert(
uname character varying(55),
fname character varying(55),
eml character varying(255),
psw character varying(265),
phonenbr character varying(55),
adrs character varying(300)
)
RETURNS table (j json) AS
$$
DECLARE
regisdate timestamp without time zone;
subsend timestamp without time zone;
statu status;
BEGIN
regisdate:=current_timestamp;
subsend:=current_timestamp+ INTERVAL '1 month';
statu='active';
INSERT INTO users
VALUES (DEFAULT,uname, fname, eml, psw, phonenbr, adrs,regisdate,subsend,statu)
ON CONFLICT (username, firstname)
DO
UPDATE SET username = EXCLUDED.username, firstname = EXCLUDED.firstname,
email = EXCLUDED.email, password = EXCLUDED.password, phonenumber = EXCLUDED.phonenumber,
address = EXCLUDED.address, registrationdate = EXCLUDED.registrationdate, subscriptionend=EXCLUDED.subscriptionend,stat = EXCLUDED.stat;
END
$$
LANGUAGE 'plpgsql';

Related

Select from Postgres into Struct array GO

So i have this struct
type MessagesStored struct {
MessagetoStream string
StreamSub string
PubName string
CreatedAt time.Time
}
And i have a table in Postgres to match the struct
CREATE TABLE "StoredMessages"
(
"Message" character varying(200) COLLATE pg_catalog."default" NOT NULL,
"PubName" character varying(50) COLLATE pg_catalog."default" NOT NULL,
"Subject" character varying COLLATE pg_catalog."default" NOT NULL,
"CreatedAt" timestamp with time zone
)
So i am trying to select all the rows where a subject matches a string and store them in an array of the struct
var OldMessages []MessagesStored
db.Query("SELECT * FROM StoredMessages WHERE Subject = ", FilterSub)
for rows.Next() {
var oldmessages MessagesStored
errrws := rows.Scan(&oldmessages.MessagetoStream, oldmessages.StreamSub, &oldmessages.PubName, &oldmessages.CreatedAt)
CheckError(errrws)
OldMessages = append(OldMessages, oldmessages)
}
```

SQL Server : concat two columns with delimiter if both not nulll else return third column

Here's a sample table
FirstName LastName UserName
----------------------------------
Bob Jones bjones
null null tomroberts
null Ricardo rricardo
Robby null robroy
George Glass gglass
I'm looking for a select that'll return
Jones, Bob
tomroberts
rricardo
robroy
Glass, George
If the first name OR last name are null or empty strings, I want to return the user name. Otherwise I want to return the "lastname, firstname"
Can someone help with the query?
Thanks!
An alternative to using a CASE statement is to use the COALESCE function which returns the first non-NULL argument.
SELECT
COALESCE(NULLIF(LastName,'') + ',' + NULLIF(FirstName,''), UserName)
FROM
MyTable
Note:- It's important to use the + to concat the columns as this will return NULL if any column contains null. The CONCAT function will return a value that contains the non-NULL parts.
You can use a CASE expression and a couple of NULLIFs to do this:
SELECT CASE WHEN NULLIF(FirstName,'') IS NULL
OR NULLIF(LastName,'') IS NULL THEN [UserName]
ELSE CONCAT(LastName, ', ', FirstName)
END
FROM dbo.YourTable;
Alternatively, if you prefer, you could use ISNULL({Value},'') = ''. Though I would personally suggest you don't allow someone to have a zero length name in your data; such a value should be stored as NULL.
You can use a simple ISNULL statement:
DECLARE #t TABLE(
firstname NVARCHAR(50)
,lastname NVARCHAR(50)
,username NVARCHAR(50)
)
INSERT INTO #t VALUES
('Bob', 'Jones', 'bjones')
,(null, null, 'tomroberts')
,(null, 'Ricardo', 'rricardo')
,('Robby', null, 'robroy')
,('George', 'Glass', 'gglass')
SELECT ISNULL(NULLIF(TRIM(lastname),'') + ', ' + NULLIF(TRIM(firstname),''), username)
FROM #t
Concatenating a string via + results in NULL whenever one of the values is NULL.

Azure SQL DW External File Format treat empty strings as NULL using Polybase

I'm using external tables to load data from csv stored in a blob to a table in Azure SQL Data Warehouse. The csv uses a string delimiter (double quote), empty strings are represented as 2 double quotes ("").
I want the empty columns to be treated as NULL in the table. The external file format I use is set up with USE_TYPE_DEFAULT = FALSE, but this does not seem to work since empty columns are imported as empty strings. And this only tends to happen when the columns are strings, numeric columns are correctly converted to NULL.
I'm also importing a different csv which does not have a string delimiter using a different external file format and those empty columns are imported as NULL. So it looks like it has something to do with the STRING_DELIMITER option.
The csv:
col1;col2;col3;col4;col5;col6
"a";"b";"c";"1";"2";"3"
"d";"";"f";"4";"";"6"
The code of the external file format:
CREATE EXTERNAL FILE FORMAT eff_string_del
WITH (
FORMAT_TYPE = DELIMITEDTEXT
,FORMAT_OPTIONS(
FIELD_TERMINATOR = ';'
,STRING_DELIMITER = '0x22'
,FIRST_ROW = 2
,USE_TYPE_DEFAULT = False)
)
Code of the table using the external file format:
CREATE EXTERNAL TABLE dbo.test (
col1 varchar(1) null
,col2 varchar(1) null
,col3 varchar(1) null
,col4 int null
,col5 int null
,col6 int null
)
WITH (
DATA_SOURCE = [EDS]
,LOCATION = N'test.csv'
,FILE_FORMAT = eff_string_del
,REJECT_TYPE = VALUE
,REJECT_VALUE = 0
)
The result when querying the external table:
SELECT *
FROM [dbo].[test]
col1 col2 col3 col4 col5 col6
---- ---- ---- ----------- ----------- -----------
a b c 1 2 3
d f 4 NULL 6
Can someone please help me explain what is happening or what I'm doing wrong?
Use USE_TYPE_DEFAULT = False in external file format.
Any NULL values that are stored by using the word NULL in the delimited text file are imported as the string 'NULL'.
For example:
CREATE EXTERNAL FILE FORMAT example_file_format
WITH (FORMAT_TYPE = DELIMITEDTEXT,
FORMAT_OPTIONS(
FIELD_TERMINATOR = ',',
STRING_DELIMITER = '"',
FIRST_ROW = 2,
USE_TYPE_DEFAULT = False)
)
Reference : https://learn.microsoft.com/en-us/sql/t-sql/statements/create-external-file-format-transact-sql?view=sql-server-2017
Have you considered adding the value NULL in that field instead of ""?
See below a test I've performed using the following code:
declare #mytable table
(id int identity primary key, column1 varchar(100))
insert into #mytable (column1) values ('test1')
insert into #mytable (column1) values ('test2')
insert into #mytable (column1) values (null)
insert into #mytable (column1) values ('test3')
insert into #mytable (column1) values (null)
select
*
from #mytable
The results looks like this:
Would this work for you?

Customer email address

I am new to sql programming. This is for a homework assignment for my database class. I have made all the table I need for the assignment I am just hung up one part. The line in the homework reads as follows:
If type is 'faculty' the email address must end up with '#xxx.edu'
This is what the table looks like:
create table customer
(
CID# char(10) primary key IDENTITY (1,1) NOT NULL,
F_name varchar(20),
M_name varchar(20),
L_name varchar(20),
type varchar(20),
street varchar(20),
city varchar(20),
state varchar(20),
zip char(5),
password varchar (20) NOT NULL,
email varchar(20) NOT NULL
Constraint CK_customer_type check (type in ('student', 'faculty'))
)
Any help someone could lend would be greatly appreciated!
This constraint would check if the email column ends with #xxx.edu.
Constraint CK_email_faculty check (
type<>'faculty' OR
CHARINDEX('#xxx.edu',email)=LEN(email)-LEN('#xxx.edu')+1
)
Remark 1: Better than checking the type being appropriate in a CHECK constraint, make a table customer_type with possible types ('student', 'faculty' ...), and have a foreign key in customer pointing a the type.
CREATE TABLE customer_type(id INT NOT NULL PRIMARY KEY,desc VARCHAR(128));
INSERT INTO customer_type(id,desc)VALUES(1,'student');
INSERT INTO customer_type(id,desc)VALUES(2,'faculty');
Have a foreign key in your customer table to point to the customer_type table:
CREATE TABLE customer(
-- ...
type INT NOT NULL,
-- ...
CONSTRAINT FK_type_customer_type FOREIGN KEY(type) REFERENCES customer_type(id)
)
You would then not insert the type description but the type identifier:
INSERT INTO customer(...,type,...)VALUES(...,1,...); -- for student
INSERT INTO customer(...,type,...)VALUES(...,3,...); -- fails, type doesn't exist
That way you save disk space, and memory when these tables are cached by SQL Server.
Remark 2: The widths of your varchar fields are very small. An email address of only 20 characters?
For your particular case a constraint like this might be ok:
Constraint CK_customer_email check (
type <> 'faculty' OR
email LIKE '%_%#_%.edu'
CHARINDEX('#xxx.edu',email)=LEN(email)-LEN('#xxx.edu')+1
)
This will allow 1+ characters, followed by #, followed by 1+ characters, followed by .edu.
However, in real life (where mean people try to insert bad e-mail addresses), validation is more complex (not all characters are allowed), so a custom function can be used. One that seems to be almost complete is provided here:
CREATE FUNCTION [dbo].[fnAppEmailCheck](#email VARCHAR(255))
--Returns true if the string is a valid email address.
RETURNS bit
as
BEGIN
DECLARE #valid bit
IF #email IS NOT NULL
SET #email = LOWER(#email)
SET #valid = 0
IF #email like '[a-z,0-9,_,-]%#[a-z,0-9,_,-]%.[a-z][a-z]%'
AND LEN(#email) = LEN(dbo.fnAppStripNonEmail(#email))
AND #email NOT like '%#%#%'
AND CHARINDEX('.#',#email) = 0
AND CHARINDEX('..',#email) = 0
AND CHARINDEX(',',#email) = 0
AND RIGHT(#email,1) between 'a' AND 'z'
SET #valid=1
RETURN #valid
END
Then, your constraint would be like this:
Constraint CK_customer_email check (
type <> 'faculty' OR
[dbo].[fnAppEmailCheck] (email) = 1
)
Add Constraint
Constraint CK_email check (email like case when type in ('faculty') then '%#xxx.edu' else email end )

Update String Columns without Using Single Quotations - General Question

UPDATE CustomerPhone
SET PhoneTypeID = 7, PhoneNumber = 999-444
WHERE CustomerID = 500 AND PhoneNumber = 9-1-1;
PhoneNumber is of type varchar(20) whereas PhoneTypeID and CustomerID are of type int. I'm running the above statement in SQL Server, it works fine.
I wonder how come it works? I thought any string value has to be put between '...'
SQL Server will CAST or CONVERT the value that you pass it, when you specify a value that isn't of type that you expect.
i.e. Try:
SELECT * FROM CustomerPhone WHERE PhoneTypeID = '7'
Here SQL Server will take your string '7' and try to convert to the appropriate type of int, smallint, tinyint (whatever appropriate).

Resources