Update when relevant rows are not present in the source table - sql

I have a merge statement like this:
MERGE DESTINATION
USING SOURCE WHERE <Some_Conditions>
WHEN MATCHED AND <Some_Conditions> THEN DELETE
WHEN MATCHED UPDATE
WHEN NOT MATCHED INSERT
This is working fine but I have one more condition i.e. I have to update the rows in destination when their entry is not present in the source.
Ex.
Source
Column1 Column2 Column3
-----------------------
A A A
B B B
Destination
Column1 Column2 Column3
-----------------------------------
B B <Some_Calculation>
D D <Some_Calculation>
Now, as there are no rows in source for D, I have to modify Column 3 in destination with some calculation. But as merge is giving only the three options of delete, update and insert when rows matched or not.
How can I implement this functionality in the above statement?
Edit
Editing question with my comments below:
In the above example which I have given above is running then It is updating B and inserting A into destination table. But I want to update D also even when it is not present in the source table

Maybe read the documentation for MERGE:
MERGE
[ TOP ( expression ) [ PERCENT ] ]
[ INTO ] <target_table> [ WITH ( <merge_hint> ) ] [ [ AS ] table_alias ]
USING <table_source>
ON <merge_search_condition>
[ WHEN MATCHED [ AND <clause_search_condition> ]
THEN <merge_matched> ] [ ...n ]
[ WHEN NOT MATCHED [ BY TARGET ] [ AND <clause_search_condition> ]
THEN <merge_not_matched> ]
[ WHEN NOT MATCHED BY SOURCE [ AND <clause_search_condition> ]
THEN <merge_matched> ] [ ...n ]
...
WHEN NOT MATCHED BY SOURCE is what you need

Related

Append values to nested jsonb array, the jsonb array may or may not exist

Below is the starting point of one of the DB entry.
{
"a": "b"
}
Below is what I did to reach to the above point.
create table if not exists testing (val jsonb);
insert into testing values('{"a":"b"}'::jsonb);
I would like to append an array of values to a key, let's say the key name to be 'errors'. If the key does not exist, I can use the below query.
update testing set "val"="jsonb_set_recursive"(val, '{errors}'::text[], '["a","b"]'::jsonb) where val->>'a'='b';
and I will have
{
"a": "b",
"errors": [
"a",
"b"
]
}
If the key "errors" already exists with some values, then I can use the below query
update testing set "val"="jsonb_set_recursive"(val, '{errors}', val->'errors' || '["a","b"]'::jsonb) where val->>'a'='b'
and I will have.
{
"a": "b",
"errors": [
"a",
"b",
"a",
"b"
]
}
The problem:
I do not know beforehand if the key "errors" exists or not.
If the key exists, I would like to append to the existing values
If the key does not exist, I would like to create the key and insert the array.
Any pointers on how to achieve this?
If I run query 4 when the key 'errors' does not exist, then the whole row value (val) seems to vanish.
You can use coalesce in the set function:
update testing set val = jsonb_set(val, '{errors}'::text[],
coalesce(val->'errors', '[]'::jsonb) || '["a","b"]'::jsonb)
where val->>'a'='b'
See fiddle

What does 'AS' mean in SQL?

Below is the synopsis of SELECT from the PostgreSQL documentation. It seems to me that sometimes we write <expression> AS <name> and sometimes it's <name> AS <expression>. In ordinary English, I tend to think <expression> AS <name> is much more common (e.g. "Address her as. Doctor Smith, please., and I'm having trouble understanding how to think about <name> AS <expression>.
How can we distinguish between where to use <name> AS <expression> and <expression> as <name>?
What are minimal obvious examples of each?
Are there parallels of each kind in ordinary language, which would make it intuitively obvious when to use what?
[ WITH [ RECURSIVE ] with_query [, ...] ]
SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]
* | expression [ [ AS ] output_name ] [, ...]
[ FROM from_item [, ...] ]
[ WHERE condition ]
[ GROUP BY expression [, ...] ]
[ HAVING condition [, ...] ]
[ WINDOW window_name AS ( window_definition ) [, ...] ]
[ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ]
[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ LIMIT { count | ALL } ]
[ OFFSET start [ ROW | ROWS ] ]
[ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ]
[ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] [...] ]
where from_item can be one of:
[ ONLY ] table_name [ * ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
( select ) [ AS ] alias [ ( column_alias [, ...] ) ]
with_query_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
function_name ( [ argument [, ...] ] ) [ AS ] alias [ ( column_alias [, ...] | column_definition [, ...] ) ]
function_name ( [ argument [, ...] ] ) AS ( column_definition [, ...] )
from_item [ NATURAL ] join_type from_item [ ON join_condition | USING ( join_column [, ...] ) ]
and with_query is:
with_query_name [ ( column_name [, ...] ) ] AS ( select | values | insert | update | delete )
TABLE [ ONLY ] table_name [ * ]
I like the question.
Here is how I see it and how I explain it to people, hope it helps:
Let's start with <expression> as <name>. The simplest analogy from real life is an abbreviation. It was created to make the code cleaner, easier to read and simply shorter. Let's imagine a scenario: we have data on all students from the Massachusetts Institute of Technology as well as Massachusetts Department of Motor Vehicles in our database and we want to find students that have speeding tickets and how much they have paid.
SELECT
government.SocialSecurityAdministration.FirstName,
government.SocialSecurityAdministration.LastName,
education.MasachusettsInstituteOfTechnology.Faculty
government.DepartmentOfMotorVehiclesTickets.TicketTotal,
government.SocialSecurityAdministration.SocialSecurityNumber
FROM
education.MasachusettsInstituteOfTechnology
INNER JOIN government.DepartmentOfMotorVehiclesTickets ON education.MasachusettsInstituteOfTechnology.SocialSecurityNumber = government.DepartmentOfMotorVehicles.SocialSecurityNumber
INNER JOIN government.SocialSecurityAdministration ON government.DepartmentOfMotorVehicles.SocialSecurityNumber = government.SocialSecurityAdministration.SocialSecurityNumber
Looks ugly, doesn't it? In real life, we've abbreviated the Massachusetts Institute of Technology to be MIT and the Department of Motor Vehicles to be DMV. I'm not aware of the official abbreviation for Social Security Administration (but we can come up with one) though we say SSN when we mean Social Security Number. Let's implement this idea:
SELECT
ssnAdm.FirstName,
ssnAdm.LastName,
ssnAdm.Faculty
dmv.TicketTotal,
ssnAdm.SocialSecurityNumber AS ssn
FROM
education.MasachusettsInstituteOfTechnology AS mit
INNER JOIN government.DepartmentOfMotorVehiclesTickets AS dmv ON mit.SocialSecurityNumber = dmv.SocialSecurityNumber
INNER JOIN government.SocialSecurityAdministration AS ssAdm ON dmv.SocialSecurityNumber = dmv.SocialSecurityNumber
Looks better now, doesn't it?
Now to the <name> as <expression> portion of it. This is done to simplify the code as well as some performance optimizations but let's focus on simplification for now. Using the same example I've used above, you might want to get/ask the following: "For every MIT student that has received a ticket I need to know the last 4 digits of their SSN, their last name, the amount of money in their bank account and their last VISA transaction amount". Yes, you work for CIA.
Let's write it:
SELECT
RIGHT(4,ts.ssn) as LastFourDigitsSsn,
ts.LastName,
bad.TotalAmount,
ISNULL(visa.TransactionAmt,'Student uses MasterCard') AS VisaTransaction
FROM
(SELECT
ssnAdm.FirstName,
ssnAdm.LastName,
ssnAdm.Faculty
dmv.TicketTotal,
ssnAdm.SocialSecurityNumber AS ssn
FROM
education.MasachusettsInstituteOfTechnology AS mit
INNER JOIN government.DepartmentOfMotorVehiclesTickets AS dmv ON mit.SocialSecurityNumber = dmv.SocialSecurityNumber
INNER JOIN government.SocialSecurityAdministration AS ssAdm ON dmv.SocialSecurityNumber = dmv.SocialSecurityNumber
) AS ts
INNER JOIN business.BankAccountsData AS bad ON ts.ssn = bad.SocialSecurityNumber
OUTER APPLY (SELECT TOP 1 TransactionAmt FROM business.VisaProcessingData vpd WHERE vpd.BankAccountID = bad.ID ORDER BY TransactionDateTime DESC) as visa
Well, looks ugly again. But what if we simplify it a bit and express certain things outside of the actual statement? That's when <name> as <expression> comes in. Let's do it:
WITH MitTicketedStudents AS (
SELECT
ssnAdm.LastName,
ssnAdm.SocialSecurityNumber as ssn,
RIGHT(4,ssnAdm.SocialSecurityNumber) as LastFourDigitsSsn
FROM
education.MasachusettsInstituteOfTechnology AS mit
INNER JOIN government.DepartmentOfMotorVehiclesTickets AS dmv ON mit.SocialSecurityNumber = dmv.SocialSecurityNumber
INNER JOIN government.SocialSecurityAdministration AS ssAdm ON dmv.SocialSecurityNumber = dmv.SocialSecurityNumber
),
LatestVisaTransactions AS (
SELECT DISTINCT
BankAccountID,
FIRST_VALUE(TransactionAmt) OVER (PARTITION BY BankAccountId ORDER BY TransactionDateTime DESC) as TransactionAmt
FROM
business.VisaProcessingData
)
-- And let's use our expressions now
SELECT
mts.LastFourDigitsSsn,
mts.LastName,
bad.TotalAmount,
ISNULL(lvt.TransactionAmt,'Student uses MasterCard') AS VisaTransaction
FROM
MitTicketedStudents mts
INNER JOIN business.BankAccountsData AS bad ON mts.ssn = bad.SocialSecurityNumber
LEFT OUTER JOIN LatestVisaTransactions lvt ON bad.ID = lvt.BankAccountID;
Looks better, doesn't it?
Conclusion: when you want to separate code you use <name> as <expression>, when you want to give something an alias to simplify code you use <expression> as <name>.
What matters is where it appears.
mytable:
mycolumn myothercolumn
----------------------
1 a
2 b
SELECT myrow.mycolumn * 2 AS mylabel
FROM (SELECT * FROM mytable) AS myrow
WHERE myrow.mycolumn > 1
mylabel
-------
4
In SELECT, we refer to the value of an expression AS some output column name ("column alias"). In FROM, we refer to (a typical row of) the value of a table expression AS some name ("table alias", "correlation name").
(It turns out that because of details of the grammar typos are less problematic if we use AS in SELECT clauses but don't use AS in FROM clauses.)
There are other uses of AS. The context also determines what they mean, and they also correspond to using using a name to refer to something.
In technical contexts it turns out not to be helpful to try to make sense of what something means based on the everyday meanings of technical terms, including making sense of what a thing is based on its name. The SQL language designers [sic] didn't choose to always have either <expression> AS <name> or <name> AS <expression>. That is just how it is. That is just how you write stuff to get your program to do stuff to stuff. (Accepted but more modern principles of computer language design do suggest more regular notations.)

Understanding SQL Definition syntax

I am pretty novice in writing SQL but would like to get better. One of the things that I have never really understood is how to interpret the construct syntax for any give object. Here is an example of OPENROWSET definition taken from https://msdn.microsoft.com/en-us/library/ms190312.aspx. I understand the pipe "|" represents "OR" but not sure about the other tags. Is there a good place to learn how to interpret this?
OPENROWSET
( { 'provider_name' , { 'datasource' ; 'user_id' ; 'password'
| 'provider_string' }
, { [ catalog. ] [ schema. ] object
| 'query'
}
| BULK 'data_file' ,
{ FORMATFILE = 'format_file_path' [ <bulk_options> ]
| SINGLE_BLOB | SINGLE_CLOB | SINGLE_NCLOB }
} )
<bulk_options> ::=
[ , CODEPAGE = { 'ACP' | 'OEM' | 'RAW' | 'code_page' } ]
[ , ERRORFILE = 'file_name' ]
[ , FIRSTROW = first_row ]
[ , LASTROW = last_row ]
[ , MAXERRORS = maximum_errors ]
[ , ROWS_PER_BATCH = rows_per_batch ]
[ , ORDER ( { column [ ASC | DESC ] } [ ,...n ] ) [ UNIQUE ]
On every page of Microsoft Docs (previously MSDN) that refers to SQL Server (even about OPENROWSET) there is a link called Transact-SQL Syntax Conventions (#Ghost post it in a comment)
That link will guide you to the page with explanation of SQL Syntax Conventions that you seek.
I think the easiest way to understand this syntax is visual Railroad Diagram.
How to use it:
Get syntax from documentation
Paste to Grammar Translator written by Colin Daley
You may need to manually correct some errors (like missing brackets, '')
Click Generate Railroad Diagram
Learn by moving from start to end
Image generated using: http://bottlecaps.de/rr/ui
You can also search for Railroad Diagrams for SQL Server.
SQL Server CREATE TABLE syntax diagrams
Most important syntax:
Optional items are enclosed in square brackets
Groups of expressions are enclosed in curly braces
A '|' is a binary operator meaning 'or'.

Query on WITH Clause in SQL Server

I have a example query(fully modified) as below:
with projects as (
select * from projectdetails
)
select * from projects
when I run the above query, it is running fine.
But when i put one more select query..it is throwing error.
select * from
(
with projects as (
select * from projectdetails
)
select * from projects
)
Error: Incorrect syntax near the keyword 'with'. If this statement is
a common table expression, an xmlnamespaces clause or a change
tracking context clause, the previous statement must be terminated
with a semicolon.
The same query runs fine in Oracle but not in SQL server.
SQL Server and Oracle have different syntax requirements for common table expressions.
In Oracle, these can come before any SELECT. In SQL Server, they need to come at the beginning of the query. Hence, you cannot have a subquery with WITH in it.
Usually, you can just move the WITH statement before the first SELECT and the query will work in both databases.
It doesn't matter what runs on Oracle, see supported SQL Server SELECT syntax
Simplified:
[ WITH { [ XMLNAMESPACES ,] [ <common_table_expression> ] } ]
SELECT select_list [ INTO new_table ]
[ FROM table_source ] [ WHERE search_condition ]
[ GROUP BY group_by_expression ]
[ HAVING search_condition ]
[ ORDER BY order_expression [ ASC | DESC ] ]
Full:
<SELECT statement> ::=
[ WITH { [ XMLNAMESPACES ,] [ <common_table_expression> [,...n] ] } ]
<query_expression>
[ ORDER BY { order_by_expression | column_position [ ASC | DESC ] }
[ ,...n ] ]
[ <FOR Clause>]
[ OPTION ( <query_hint> [ ,...n ] ) ]
<query_expression> ::=
{ <query_specification> | ( <query_expression> ) }
[ { UNION [ ALL ] | EXCEPT | INTERSECT }
<query_specification> | ( <query_expression> ) [...n ] ]
<query_specification> ::=
SELECT [ ALL | DISTINCT ]
[TOP ( expression ) [PERCENT] [ WITH TIES ] ]
< select_list >
[ INTO new_table ]
[ FROM { <table_source> } [ ,...n ] ]
[ WHERE <search_condition> ]
[ <GROUP BY> ]
[ HAVING < search_condition > ]
If you don't know how to read this syntax directly you can generate Railroad Diagrams for every syntax definition from BOL examples below:
The query which you are trying to execute is called Common Table Expression and the the syntax which you are using is not the proper way of using it ie, you cannot use the WITH clause inside the select statement.
with projects as (
select * from projectdetails
)
select * from projects
The above query is fine and will work and the next query which you posted is neither correct and neither it is making sense as it is looking as if you are trying to do the same thing as above.
As other two answers speak about the syntax of CTE and their limitation, here is a way you would do this what you are trying to do, Multiple CTEs.
with projectdetails as (
select * from projectdetails
),projects AS
select * from projects
)
SELECT * FROM projects --<-- here you select from Projects or projectdetails

Pervasive SQL Update with top

I am looking to do this:
update aTable
set aField = 'value'
where aTable.Id in (select top 400 Id from aTable order by dateField) .
-- This will run, but it only updates the first id it gets to in the IN clause
or this:
update top 400 aTable set aField = 'value' order by dateField
-- This will not run
But, it has to work in V10 of Pervasive...
A similiar workaround would be sufficient too!
Background, I am trying to update a (number(which is variable) of items)'s field with a value ordered by a date field.
Here is Pervasive's Update help (I do not see the TOP reserved word in there):
UPDATE < table-name | view-name > [ alias-name ]
SET column-name = < NULL | DEFAULT
| expression | subquery-
expression > [ , column-name = ... ]
[ FROM table-reference [, table-reference ] ...
[ WHERE search-condition ]
table-name ::= user-defined-name
view-name ::= user-defined-name
alias-name ::= user-defined-name (Alias-name is not allowed if a
FROM clause is used. See FROM Clause .)
table-reference ::= { OJ outer-join-definition }
| [db-name.]table-name [ [ AS ] alias-name ]
| [db-name.]view-name [ [ AS ] alias-name ]
| join-definition | ( join-definition )
| ( table-subquery )[ AS ] alias-name [ (column-name [ , column-name
]... ) ]
I did some testing and the best way to get this working is to create a view:
create view View1 as select Id from
aTable order by dateField;
Then use the view in the Update:
update aTable set aField = 'value'
where aTable.Id in (select top 400
a.Id from View1 a );
I've tested with PSQL v11 but it should also work with PSQL v10.