Convert view from SQL Server to Redshift - sql-server

I have view in SQL Server, that I need to convert to Redshift
Here is the view:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.BE_ContactRelationsDual') AND type in (N'V'))
DROP VIEW public.contactrelationsdual;
GO
CREATE VIEW public.contactrelationsdual
WITH SCHEMABINDING
AS
SELECT CASE d.id
WHEN 0 THEN
cr.contactId
ELSE
cr.relatedContactId
END me,
CASE d.id
WHEN 0 THEN
cr.relatedContactId
ELSE
cr.contactId
END him,
cr.id,
cr.permissionId
FROM public.contact_relations cr
CROSS JOIN
public.system_dual d
WHERE cr.contactId > 0
AND cr.relatedContactId > 0
AND cr.deletedDate IS NULL
GO
CREATE UNIQUE CLUSTERED INDEX
UX_BE_ContactRelationsDual_Me_Him
ON public.contactrelationsdual (me, him)
GO
I need to convert it to redshift. I have 2 problems:
This:
CREATE VIEW public.contactrelationsdual
WITH SCHEMABINDING
and this:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id =
OBJECT_ID(N'dbo.BE_ContactRelationsDual') AND type in (N'V')) DROP
VIEW public.contactrelationsdual; GO
How I can correctly convert it to Redshift.

Remove with schemabinding:
CREATE VIEW public.contactrelationsdual AS
SELECT (CASE d.id WHEN 0 THEN cr.contactId ELSE cr.relatedContactId
END) as me,
(CASE d.id WHEN 0 THEN cr.relatedContactId ELSE cr.contactId
END) as him,
cr.id,
cr.permissionId
FROM public.contact_relations cr CROSS JOIN
public.system_dual d
WHERE cr.contactId > 0 AND
cr.relatedContactId > 0 AND
cr.deletedDate IS NULL;
This is pretty standard SQL and should work on almost any database.
The IF EXISTS part can be replaced with:
drop view if exists public.contactrelationsdual;
There is no need for a clustered index in Redshift.

Related

Create View using EF Core

Following guidance in this blog post, I've added an ef core (dotnet 6) migration into which I've pasted the t-sql to create a view.
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(#"
exec('create view [dbo].[vwSpotlightExtract] as
with
exceptions as (
select id from dbo.Application
where CTaxNumber in (
select
app.CTaxNumber
from dbo.Application app
group by app.CTaxNumber
having count(*) > 1
)
union
-- duplicate bank account
select id from dbo.Application where concat(BankSortCode, ':', BankAccountNumber) in (
select concat(app.BankSortCode, ':', app.BankAccountNumber)
from dbo.Application app
where app.BankAccountNumber is not null
group by concat(app.BankSortCode, ':', app.BankAccountNumber)
having count(*) > 1
)
union
-- duplicate uprn
select id from dbo.Application where uprn in (
select app.uprn from dbo.Application app
group by app.uprn having count(*)>1
)
),
LastAppStatus as (
select app.Id, app.UTRN,
max(rh.Id) LastRebateHistoryID
from dbo.Application app
inner join dbo.RebateHistory rh on app.ID = rh.ApplicationId
where rh.ApplicationId is not null --and rh.ApplicationId not in (select ID from exceptions)
and app.RequestType = 0 -- BACS
and ISNULL(app.PaymentStopped, 0) = 0 -- Payment NOT Stopped
--and app.id not in (select id from exceptions) -- to prevent sending stuff to spotlight
group by app.Id, app.UTRN
)
select
app.UTRN [Application Number],
'Personal' [Personal or Business Bank Account? (required)], -- always Personal
REPLACE(app.BankSortCode, '-', '') [Sort code (required)],
app.BankAccountNumber [Account number (required)],
NULL [Business name (required if business)],
app.AccountPayerFirstName [First Name (required if personal)],
app.AccountPayerSurname [Surname (required if personal)],
CASE WHEN LEN(CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx)) > 0 and LEN (CONCAT(addr.SAO_End_No, addr.SAO_End_Sfx)) > 0 then CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx, '-', addr.SAO_End_No, addr.SAO_End_Sfx)
WHEN LEN(CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx)) > 0 and LEN (CONCAT(addr.SAO_End_No, addr.SAO_End_Sfx)) = 0 then CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx)
WHEN LEN(CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx)) = 0 and LEN (CONCAT(addr.SAO_End_No, addr.SAO_End_Sfx)) > 0 then CONCAT(addr.SAO_End_No, addr.SAO_End_Sfx)
else NULL end as
[Flat number (optional)], -- secondary addressable number or name
case when LEN(addr.PAO_Desc) > 0 then addr.PAO_Desc
when LEN(addr.SAO_Desc) < 0 then addr.SAO_Desc
ELSE NULL
end as [Building name (optional)],-- primary addressable name
CASE WHEN LEN(CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx)) > 0 and LEN (CONCAT(addr.PAO_End_No, addr.PAO_End_Sfx)) > 0 then CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx, '-', addr.PAO_End_No, addr.PAO_End_Sfx)
WHEN LEN(CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx)) > 0 and LEN (CONCAT(addr.PAO_End_No, addr.PAO_End_Sfx)) = 0 then CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx)
WHEN LEN(CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx)) = 0 and LEN (CONCAT(addr.PAO_End_No, addr.PAO_End_Sfx)) > 0 then CONCAT(addr.PAO_End_No, addr.PAO_End_Sfx)
end [Building number (optional)],
addr.Street_Name [Street name (required)],
addr.Postcode [Address postcode (required)],
case when app.AccountType = 0 then 'Single'
when app.AccountType = 1 then 'Joint'
end as [Single or Joint account type (if personal)], -- new field coming
convert(varchar, app.AccountPayerDOB, 103) [Date of birth (if personal)],
NULL [Limited or non-limited (if business)],
NULL [Company registration number (if business)]
, ec.EventName
, app.ID
from dbo.Application app
left outer join dbo.AcademyNonDDPayers nondd on app.CTaxNumber = nondd.CTaxNumber
inner join dbo.CTaxAddress addr on RIGHT('000000000000'+ISNULL(app.uprn,''),12) = addr.uprn and nondd.CTaxPropertyRef = addr.CTaxPropertyRef
inner join LastAppStatus las on app.ID = las.ID
inner join dbo.RebateHistory rh on las.LastRebateHistoryID = rh.ID
inner join dbo.EventCode ec on rh.EventCodeId = ec.ID
where ec.ID = 11')
");
}
This causes a failure in the release pipeline. If I take the content of the sql script from the deployment artefact and paste into SSMS then I see the following error:
I'm not sure why this is because the create view statement seems to be correctly wrapped with begin and end statements:
CREATE VIEW can’t be inside IF/BEGIN logic; it has to be the only statement in the batch. The typical workaround is to create a script like this:
IF OBJECT_ID(N'dbo.viewname', N'V') IS NULL
BEGIN
EXEC sys.sp_executesql N'CREATE VIEW dbo.viewname AS SELECT 1;';
END
GO
ALTER VIEW dbo.viewname
AS
... real view code ...
In newer versions of SQL Server, you can just do:
CREATE OR ALTER VIEW dbo.viewname
AS
... real view code ...
But that still has to live in its own batch. Meaning it can't be inside IF/BEGIN unless you use dynamic SQL for the whole view. e.g.:
IF (some condition)
BEGIN
EXEC sys.sp_executesql N'CREATE VIEW dbo.viewname
AS
... all that view code ...;';
END
Whether you'll be able to generate any of those forms from EF core, I just don't know. Sometimes we expect an ORM to do absolutely everything but, usually, it is only capable of a very small subset.
I'm not sure of the logic you're trying to get to anyway. (If a row doesn't exist in some table, create a view? That will only work once.)

How to properly join when comparing the difference between two tables pertaining to specific fields

I'm having problems writing this query. So I'm comparing a temp table to a table within our database. I want to find any records that don't have the same Case_Number the same Id_Number combination between the two table. The query I am using only provides me one or the other depending on how I join them. If I join by Case_Number, it returns the Case_Number records that do not match between the two tables. If I join by Id_Number, it will return the Id_Numbers that do not match between the two tables.. Is there a way to join by both Case_Number and ID_Number so that the query returns both? I would also like to know if it would be possible for me to include an "If Exist" to the query? Code below
SELECT T1.Case_Number, T1.Id_Number, T1.FirstDate, T1.LastDate, T2.Case_Number, T2.Id_Number, T2.FirstDate, T2.LastDate
FROM dbo.table T
inner join #TempTable T2
on T1.Id_Number = T2.Id_Number
--on T1.Case_Number = T2.Case_Number
where T1.LastDate is null
and T1.Case_Number <> T2.Case_Number
OR T1.Id_number <> T2.Id_Number
Use or in inner join statement
SELECT T1.Case_Number,
T1.Id_Number,
T1.FirstDate,
T1.LastDate,
T2.Case_Number,
T2.Id_Number,
T2.FirstDate,
T2.LastDate
FROM dbo.table T
INNER JOIN #TempTable T2 ON (T1.Id_Number = T2.Id_Number
OR T1.Case_Number = T2.Case_Number)
WHERE T1.LastDate IS NULL
AND T1.Case_Number <> T2.Case_Number
OR T1.Id_number <> T2.Id_Number
This should find both - firsst colum tells you what is the case
(
SELECT 'in Table not in Temp' as R,
T1.Case_Number as 'T1.Case_Number',
T1.Id_Number as 'T1.Id_Number',
T1.FirstDate as 'T1.FirstDate',
T1.LastDate as 'T1.LastDate',
null as 'T2.Case_Number',
null as 'T2.Id_Number',
null as 'T2.FirstDate',
null as 'T2.LastDate'
FROM dbo.table T1
where not exists (
select 1
from #TempTable T2
where T1.Case_Number == T2.CaseNumber
and T1.Id_Number == T2.Id_Number)
)
UNION ALL
(
SELECT 'in temp, not in list' as R
null as 'T1.Case_Number',
null as 'T1.Id_Number',
null as 'T1.FirstDate',
null as 'T1.LastDate',
T2.Case_Number as 'T2.Case_Number',
T2.Id_Number as 'T2.Id_Number',
T2.FirstDate as 'T2.FirstDate',
T2.LastDate as 'T2.LastDate'
FROM #TempTable T2
where not exists (
select 1
from dbo.table T1
where T1.Case_Number == T2.CaseNumber
and T1.Id_Number == T2.Id_Number )
)
Add the T1.LastDate is null - not sure where you want it.
You probably need to tweak the column names , I switched T1/T2 for 2nd statement so column will be the same table all the time, but names have duplicates.

Creation of DML Trigger (for Delete)

I have four tables in my database: Products, Printers, PC, Laptops.
In all these tables I have attribute "Model". All models from: Printers, PC and Laptops is in table Products. I need to create a Trigger that interdicts deletion of models that have the price >300 and this models are produced by the "A" manufacturer.
select distinct
products.model
from
products
left join
PC on products.model = PC.model
left join
laptops on products.model = laptops.model
left join
printers on products.model = printers.model
where
manufacturer = 'A'
and (PC.price > 300 or laptops.price > 300 or printers.price > 300);
This SELECT returns the models that satisfy this conditions. I tried to create a DML Trigger.
[1]
CREATE TRIGGER TASK3
ON products
FOR DELETE
AS
IF ((SELECT deleted.model FROM deleted)
IN (select products.model
from products
left join PC on products.model = PC.model
left join laptops on products.model = laptops.model
left join printers on products.model = printers.model
where manufacturer = 'A'
and (PC.price > 300 or laptops.price > 300 or printers.price > 300)))
begin
raiserror ( 'This model can't be deleted, because price is greater than 300 and manufacturer is 'A',2,1)
rollback transaction;
end
else
print 'That model will be deleted'
I tried to compare the attribute value that will be deleted (from deleted table) with values returned by SELECT like [1], in case if the value is met in the list of models that respect this condition (price>300 && manufacturer = 'A') then the Trigger to interdict his deletion.
Note that in SQL Server, deletions can affect multiple records. So, your first subquery can cause a problem of returning too many records.
If you want to prevent the execution of the entire delete when even one record fails, then:
if (exists (select 1
from deleted d
where d.manufacturer = 'A' and
(exists (select 1 from pc p where p.model = d.model and p.price > 300) or
exists (select 1 from laptops l where l.model = d.model and l.price > 300) or
exists (select 1 from printers p where p.model = d.model and p.price > 300)
)
)
)
begin
raiseerror . . .
end;
)

Check if record exists while inserting using User defined table type sql server

I am trying to bulk insert the records in sql server. I am using User Defined Table type to pass the collection of records from my .net application. Please take a look at the insert query below.
INSERT INTO MachineItems([Name],[Price],[Quantity],[ItemGroupID],[SubGroup] ,[IsDefault],
[IsRemovable],[MachineTypeID],[ItemType],[CreatedBy],[CreatedOn] )
SELECT mi.Name
,mi.Price
,mi.Quantity
,(SELECT ID from ItemGroups WHERE NAME=mi.ItemGroup) as ID
,mi.SubGroup,
CASE
WHEN mi.IsDefault ='Yes' THEN 1
WHEN mi.IsDefault ='No' THEN 0
WHEN mi.IsDefault IS NULL THEN 0
END ,
CASE
WHEN mi.IsRemovable ='Yes' THEN 1
WHEN mi.IsRemovable ='No' THEN 0
END ,
(SELECT ID from MachineTypes WHERE Name=mi.MachineType),
(SELECT ID from MachineItemTypes WHERE Name=mi.ItemType),
mi.CreatedBy
,mi.CreatedOn
FROM #MachineItems mi
What i want to do is put the check before inserting the records , Whether record with [MachineTypeID] and [Name] already exists in table or not. If it does not exists then insert Eles Update the record.
How can i do that with User Defined Table Type ?
You should use the MERGE command rather than a straight insert. What you are wanting to do is not really specific to User-Defined Table Types.
It would be better / more efficient if you joined the 3 subtables rather than having subqueries for columns which will execute per-row.
Example:
MERGE MachineItems AS Target
USING (SELECT mi.Name,
mi.Price,
mi.Quantity,
ig.ID, -- ItemGroupID
mi.SubGroup,
CASE
WHEN mi.IsDefault ='Yes' THEN 1
WHEN mi.IsDefault ='No' THEN 0
WHEN mi.IsDefault IS NULL THEN 0
END, -- IsDefault
CASE
WHEN mi.IsRemovable ='Yes' THEN 1
WHEN mi.IsRemovable ='No' THEN 0
END, -- IsRemovable
mt.ID, -- MachineTypeID
mit.ID, -- ItemType
mi.CreatedBy,
mi.CreatedOn
FROM #MachineItems mi
INNER JOIN ItemGroups ig
ON ig.[Name] = mi.ItemGroup
INNER JOIN MachineTypes mt
ON mt.[Name] = mi.MachineType
INNER JOIN MachineItemTypes mit
ON mit.[Name] = mi.ItemType) AS Source (
[Name],[Price],[Quantity],[ItemGroupID],[SubGroup],[IsDefault],
[IsRemovable],[MachineTypeID],[ItemType],[CreatedBy],[CreatedOn])
ON (
Target.[MachineTypeID] = Source.[MachineTypeID]
AND Target.[Name] = Source.[Name]
)
WHEN MATCHED THEN
UPDATE SET Price = Source.Price,
Quantity = Source.Quantity,
ItemGroupID = Source.ItemGroupID,
SubGroup = Source.SubGroup,
IsDefault = Source.IsDefault,
IsRemovable = Source.IsRemovable,
MachineTypeID = Source.MachineTypeID,
ItemType = Source.ItemType,
CreatedBy = Source.CreatedBy,
CreatedOn = Source.CreatedOn
WHEN NOT MATCHED BY TARGET THEN
INSERT ([Name],[Price],[Quantity],[ItemGroupID],[SubGroup] ,[IsDefault],
[IsRemovable],[MachineTypeID],[ItemType],[CreatedBy],[CreatedOn])
VALUES (Source.[Name], Source.[Price], Source.[Quantity], Source.[ItemGroupID],
Source.[SubGroup], Source.[IsDefault], Source.[IsRemovable],
Source.[MachineTypeID], Source.[ItemType], Source.[CreatedBy],
Source.[CreatedOn]);
You can Use Merge Here
Using Merge
You can Insert if Not Exists
You can Delete if Already Exists
You can Update if Already Exists
MERGE MachineItems
USING #MachineItems ON MachineItems.id = #MachineItems.id
and MachineItems.Name=#MachineItems.Name
WHEN NOT MATCHED THEN
INSERT INTO MachineItems([Name],[Price],[Quantity],[ItemGroupID],[SubGroup]
,[IsDefault],
[IsRemovable],[MachineTypeID],[ItemType],[CreatedBy],[CreatedOn] )
SELECT mi.Name
,mi.Price
,mi.Quantity
,(SELECT ID from ItemGroups WHERE NAME=mi.ItemGroup) as ID
,mi.SubGroup,
CASE
WHEN mi.IsDefault ='Yes' THEN 1
WHEN mi.IsDefault ='No' THEN 0
WHEN mi.IsDefault IS NULL THEN 0
END ,
CASE
WHEN mi.IsRemovable ='Yes' THEN 1
WHEN mi.IsRemovable ='No' THEN 0
END ,
(SELECT ID from MachineTypes WHERE Name=mi.MachineType),
(SELECT ID from MachineItemTypes WHERE Name=mi.ItemType),
mi.CreatedBy
,mi.CreatedOn
FROM #MachineItems mi

Finding difference between 2 tables in MS Access or SQL Server

I have 2 Excel files which I imported into MS Access as two tables. These two tables are identical but imported on different dates.
Now, how can I find out what rows and what fields are updated on the later date? Any help would be highly appreciated.
Finding Inserted records is easy
select * from B where not exists (select 1 from A where A.pk=B.pk)
Finding Deleted records is just as easy
select * from A where not exists (select 1 from B where A.pk=B.pk)
Finding Updated records is a pain. The following rigorous query assumes you have nullable columns and it should work in all situations.
select B.*
from B
inner join A on B.pk=A.pk
where A.col1<>B.col1 or (IsNull(A.col1) and not IsNull(B.col1)) or (not IsNull(A.col1) and IsNull(B.col1))
or A.col2<>B.col2 or (IsNull(A.col2) and not IsNull(B.col2)) or (not IsNull(A.col2) and IsNull(B.col2))
or A.col3<>B.col3 or (IsNull(A.col3) and not IsNull(B.col3)) or (not IsNull(A.col3) and IsNull(B.col3))
etc...
If the columns are defined as NOT NULL then the query is much simper, just remove all the NULL tests.
If the columns are nullable but you can identify a value that will never appear in the data, then use a simple comparison like:
Nz(A.col1,neverAppearingValue)<>Nz(B.col1,neverAppearingValue)
I believe this should be as simple as running a query like this:
SELECT *
FROM Table1
JOIN Table2
ON Table1.ID = Table2.ID AND Table1.Date != Table2.Date
One way to do this is by unpivoting both tables, so you get a new table with , , . Note, though, that you have to take types into account.
For example, the following gets differences in fields:
with oldt as (select id, col, val
from <old table> t
unpivot (val for col in (<column list>)) unpvt
),
newt as (select id, col, val
from <new table> t
unpivot (val for col in (<column list>)) unpvit
)
select *
from oldt full outer join newt on oldt.id = newt.id
where oldt.id is null or newt.id is null
The alternative way with a join is rather cumbersome. This version shows whether columns are added, deleted, and which columns changed if any:
select *
from (select coalesce(oldt.id, newt.id) as id,
(case when oldt.id is null and newt.id is not null then 'ADDED'
when oldt.id is not null and newt.id is null then 'DELETED'
else 'SAME'
end) as stat,
(case when oldt.col1 <> newt.col1 or oldt.col1 is null and newt.col1 is null
then 1 else 0 end) as diff_col1,
(case when oldt.col2 <> newt.col2 or oldt.col2 is null and newt.col2 is null
then 1 else 0 end) as diff_col2,
...
from <old table> oldt full outer join <new table> newt on oldt.id = newt.id
) c
where status in ('ADDED', 'DELETED') or
(diff_col1 + diff_col2 + ... ) > 0
It does have the advantage of working for any data types.
(Select * from OldTable Except Select *from NewTable)
Union All
(Select * from NewTable Except Select *from OldTable)

Resources