Run TSQL script on Redshift - tsql

I have a script that I once created and have been using on TSQL for years. It creates a calendar dimension.
I converted my script from T-SQL to Redshift using http://www.sqlines.com/online. I executed it through DBeaver. It throws all kinds of errors.
I tried correcting it but then found out Redshift does not support variables.
Does anyone know whether (and if so, how) I can execute the script below on Redshift?
The script below is pure T-SQL, do not hesitate to use it, change it, improve it out- or inside the context of this question.
Parts of it are dutch but using the translations below you should be easily able to understand what it is doing
dag = day
maand = month
jaar = year
werkdag = workingday
lang = long
kort = short
teller = counter
naam = name
feestdag = holiday
set nocount on
declare #begindatum as datetime = dateadd(year,-150,getdate())
declare #aantal_jaren as int = 1 -- betekent x - 100 jaren in de toekomst
declare #einddatum as datetime = dateadd(year,#aantal_jaren,#begindatum)
declare #rondedatum as datetime = #begindatum
declare #print as varchar(max)
set language dutch
select 'Start', getdate()
Declare #kalender as table (
[PeriodeID] [int] NOT NULL,
[Datum] [date] NOT NULL,
[DatumMedium] [varchar](25) NULL,
[DatumLang] [varchar](25) NULL,
[DatumExtraLangNOT] [varchar](25) NULL,
[DatumVoluit] [varchar](50) NULL,
[DagVanJaar] [int] NULL,
[DagVanWeek] [int] NULL,
[DagVanMaand] [int] NULL,
[DagNaamKort] [nvarchar](4000) NULL,
[DagNaamLang] [nvarchar](30) NULL,
[WerkDag] [int] NULL,
[Weekend] [int] NOT NULL,
[Feestdag] [varchar](25) NULL,
[Werkdagteller] [int] NOT NULL,
[WeekNr] [int] NULL,
[Iso_WeekNr] [int] NULL,
[Maand] [int] NULL,
[MaandNaamKort] [nvarchar](30) NULL,
[MaandNaamLang] [nvarchar](4000) NULL,
[Kwartaal] [int] NULL,
[KwartaalNaamKort] [varchar](2) NULL,
[KwartaalNaamLang] [varchar](25) NULL,
[Jaar] [int] NOT NULL,
[JaarKwartaal] int NULL,
[JaarKwartaalNaam] [varchar](7) NULL,
[JaarWeek] [int] NULL,
[JaarWeekNaam] [varchar](25) NULL,
[JaarIso_Week] [int] NULL,
[JaarIso_WeekNaam] [varchar](25) NULL,
[JaarMaand] [int] NULL,
[JaarMaandNaamKort] [varchar](25) NULL,
[JaarMaandNaamMedium] [varchar](25) NULL,
[JaarMaandNaamLang] [varchar](25) NULL,
PRIMARY KEY (PeriodeID)
)
While #rondedatum <= #einddatum
BEGIN
Insert into #kalender
SELECT
cast(#rondedatum as int) as PeriodeID
,cast(#rondedatum as date) as Datum
,cast(DATEPART ( Day , #rondedatum ) as varchar(2)) + ' ' + Format(#rondedatum,'MMM') + ' '+ cast(year(#rondedatum) as varchar(4)) as DDMMMJJJJ
,cast(DATEPART ( Day , #rondedatum ) as varchar(2)) + ' ' + Format(#rondedatum,'MMMM') + ' '+ cast(year(#rondedatum) as varchar(4)) as DatumLang
,Format(#rondedatum,'ddd') + ' ' +cast(DATEPART ( Day , #rondedatum ) as varchar(2)) + ' ' + Format(#rondedatum,'MMMM') + cast(year(#rondedatum) as varchar(4)) as DatumExtraLang
,DATENAME(dw,#rondedatum) + ' ' +cast(DATEPART ( Day , #rondedatum ) as varchar(2)) + ' ' + Format(#rondedatum,'MMMM') + ' '+cast(year(#rondedatum) as varchar(4)) as DatumVoluit
,DATEPART ( DAYOFYEAR , #rondedatum ) as DagVanJaar
,DATEPART ( WEEKDAY , #rondedatum ) as DagVanWeek
,DATEPART ( Day , #rondedatum ) as DagVanMaand
,Format(#rondedatum,'ddd') as DagNaamKort
,DATENAME(dw,#rondedatum) as DagNaamLang
,IIF(DATEPART ( WEEKDAY , #rondedatum ) >=1 AND DATEPART ( WEEKDAY , #rondedatum ) <= 5,1,0) as WerkDag
,IIF(DATEPART ( WEEKDAY , #rondedatum ) in (6,7),1,0) as Weekend
,'' as Feestdag
, 0 as Werkdagteller
,DATEPART ( WEEK , #rondedatum ) as WeekNr
,DATEPART ( ISO_WEEK , #rondedatum ) as Iso_WeekNr
,DATEPART ( MONTH , #rondedatum ) as Maand
,Format(#rondedatum,'MMM') as MaandNaamKort
,DATENAME(mm,#rondedatum) as MaandNaamLang
,DATEPART ( QUARTER , #rondedatum ) as Kwartaal
,'K' + cast(DATEPART ( QUARTER , #rondedatum ) as varchar(1)) as KwartaalNaamKort
,cast(DATEPART ( YEAR , #rondedatum ) as varchar(4)) + ' K' + cast(DATEPART ( QUARTER , #rondedatum ) as varchar(1)) as KwartaalNaamLang
,DATEPART ( YEAR , #rondedatum ) as Jaar
,DATEPART ( YEAR , #rondedatum ) *100 + DATEPART ( QUARTER , #rondedatum ) as JaarKwartaal
,cast(DATEPART ( YEAR , #rondedatum ) as varchar(4)) + ' K' + cast(DATEPART ( QUARTER , #rondedatum ) as varchar(1)) as JaarKwartaalNaam
,DATEPART ( YEAR , #rondedatum )*100+ DATEPART ( WEEK , #rondedatum ) as JaarWeek
,Cast(DATEPART ( YEAR , #rondedatum ) as varchar(4)) + ' - ' + cast(DATEPART ( WEEK , #rondedatum ) as varchar(2)) as JaarWeekNaam
,DATEPART ( YEAR , #rondedatum )*100+ DATEPART ( ISO_WEEK , #rondedatum ) as JaarIso_Week
,Cast(DATEPART ( YEAR , #rondedatum ) as varchar(4)) + ' - ' + cast(DATEPART ( iso_WEEK , #rondedatum ) as varchar(2)) as JaarIso_WeekNaam
,DATEPART ( YEAR , #rondedatum )*100+ DATEPART ( MONTH , #rondedatum ) as JaarMaand
,Cast(DATEPART ( YEAR , #rondedatum ) as varchar(4)) + ' - ' + cast(DATEPART ( MONTH , #rondedatum ) as varchar(2)) as JaarMaandNaamKort
,Format(#rondedatum,'MMM') + ' '+ Cast(DATEPART ( YEAR , #rondedatum ) as varchar(4)) as JaarMaandNaamMedium
,Format(#rondedatum,'MMMM') + ' '+ Cast(DATEPART ( YEAR , #rondedatum ) as varchar(4)) as JaarMaandNaamLang
set #rondedatum = dateadd(day,1,#rondedatum)
--select cast(#rondedatum as date)
END
select 'Loop is klaar', getdate()
select 'Kalender Updaten (Feestdagen)', getdate()
update #kalender
set
werkdag = CASE WHEN DagVanMaand in (31) and maand = 12 THEN 0
WHEN DagVanMaand in (1) and maand = 1 THEN 0
WHEN DagVanMaand in (25,26) and maand = 12 THEN 0
WHEN DagVanMaand in (5) and maand = 5 AND Jaar < 2000 THEN 0
WHEN DagVanMaand in (5) and maand = 5 AND Jaar % 5 = 0 and jaar >= 2000 THEN 0
WHEN Jaar >= 2014 and dagvanMaand = 27 and maand = 4 THEN 0
WHEN Jaar >= 1898 and jaar <= 2013 and dagvanMaand = 30 and maand = 4 THEN 0
END,
feestdag = CASE WHEN DagVanMaand in (31) and maand = 12 THEN 'Oudjaar'
WHEN DagVanMaand in (1) and maand = 1 THEN 'Nieuwjaar'
WHEN DagVanMaand in (25,26) and maand = 12 THEN 'Kerst'
WHEN DagVanMaand in (5) and maand = 5 AND Jaar < 2000 THEN 'Bevrijdingsdag'
WHEN DagVanMaand in (5) and maand = 5 AND Jaar % 5 = 0 and jaar >= 2000 THEN 'Bevrijdingsdag'
WHEN Jaar >= 2014 and dagvanMaand = 27 and maand = 4 THEN 'Koningsdag'
WHEN Jaar >= 1898 and jaar <= 2013 and dagvanMaand = 30 and maand = 4 THEN 'Koninginnedag'
END
select 'Kalender Updaten (Werkdagen + werkdagtellers)', getdate()
--Werkdagteller runnen
declare #i as int = 0
declare #current_value int = 0
declare #werkdag as int
while #i < (select max(periodeid) from #kalender)
BEGIN
update #kalender
set werkdagteller =iif(werkdag = 0 , #current_value , #current_value + 1)
from #kalender
where PeriodeID = #i
set #werkdag = (select werkdag from #kalender where periodeid = #i)
set #current_value = iif(#werkdag = 0 , #current_value , #current_value + 1)
set #i = #i +1
set #print = cast(#i as varchar(max)) + ' van ' + cast((select max(periodeid) from #kalender) as varchar(max))
print #print
END
select 'Inserten in fysieke tabel', getdate()
IF OBJECT_ID (N'shared.dim_kalender', N'U') IS NOT NULL
BEGIN
drop table shared.dim_kalender
END
CREATE TABLE [shared].[dim_kalender](
[PeriodeID] [int] DEFAULT 0,
[Datum] [date] NOT NULL,
[DatumMedium] [nvarchar](25) DEFAULT '',
[DatumLang] [nvarchar](25) DEFAULT '',
[DatumExtraLang] [nvarchar](25) DEFAULT '',
[DatumVoluit] [nvarchar](50) DEFAULT '',
[DagVanJaar] [int] DEFAULT 0,
[DagVanWeek] [int] DEFAULT 0,
[DagVanMaand] [int] DEFAULT 0,
[DagNaamKort] [nvarchar](4000) DEFAULT '',
[DagNaamLang] [nvarchar](30) DEFAULT '',
[WerkDag] [int] DEFAULT 0,
[Weekend] [int] DEFAULT 0,
[Feestdag] [nvarchar](25) DEFAULT '',
[Werkdagteller] [int] DEFAULT 0,
[WeekNr] [int] DEFAULT 0,
[Iso_WeekNr] [int] DEFAULT 0,
[Maand] [int] DEFAULT 0,
[MaandNaamKort] [nvarchar](30) DEFAULT '',
[MaandNaamLang] [nvarchar](4000) DEFAULT '',
[Kwartaal] [int] DEFAULT 0,
[KwartaalNaamKort] [nvarchar](2) DEFAULT '',
[KwartaalNaamLang] [nvarchar](25) DEFAULT '',
[Jaar] [int] DEFAULT 0,
[JaarKwartaal] [int] DEFAULT 0,
[JaarKwartaalNaam] [nvarchar](7) DEFAULT '',
[JaarWeek] [int] DEFAULT 0,
[JaarWeekNaam] [nvarchar](25) DEFAULT '',
[Jaar_IsoWeek] [int] DEFAULT 0,
[Jaar_IsoWeekNaam] [nvarchar](25) DEFAULT '',
[JaarMaand] [int] DEFAULT 0,
[JaarMaandNaamKort] [nvarchar](25) DEFAULT '',
[JaarMaandNaamMedium] [nvarchar](25) DEFAULT '',
[JaarMaandNaamLang] [nvarchar](25) DEFAULT ''
) ON [PRIMARY]
INSERT INTO [shared].[dim_kalender]
select * from #kalender

No tsql on Redshift. No easy way to create a calendar table using Redshift. Redshift does not support any way to create rows on a table e.g. generate_series() is not supported
Very easy to create a calendar table elsewhere (e.g. excel or python) and upload to redshift.
Take a look here for a sample table that AWS provides
https://docs.aws.amazon.com/redshift/latest/gsg/rs-gsg-create-sample-db.html
Or you can search on stackoverflow and find a number of solutions.
My preferred approach is something like this:
First, create your table, (with diststyle all)
then..
insert into dwh.dim_date
select thisdate as date,
date_part(year,thisdate) as year,
date_part(mm,thisdate) as month,
to_char(thisdate, 'Mon') as month_name,
date_part(day,thisdate) as day_of_mon,
date_part(dow,thisdate) as day_of_week_num,
to_char(dat, 'thisdate') as day_of_week,
date_part(week,thisdate) as week_of_year,
date_part(doy,thisdate) as day_of_year,
decode(date_part(dow,thisdate),0,true,6,true,false) as is_weekend
from
(select
trunc(dateadd(day, ROW_NUMBER () over ()-1, '1960-01-01')) as thisdate
from YOUR_BIG_TABLE
);
where YOUR_BIG_TABLE is a table you have on redshift with a where clause to set the right number of future dates after year 1900

Related

Find closest second to a given datetimeoffset

I understand that there have been many resources about this already, for example Find closest date in SQL Server but I don't think this is a duplicate because it goes into much more depth due to the requirement.
I need to find a record closest to a given date/time/offset either in the past or in the future to the nearest second.
IF ( OBJECT_ID( N'[dbo].[MYTIMETABLE]' ) IS NOT Null )
DROP TABLE [dbo].[MYTIMETABLE];
GO
CREATE TABLE [dbo].[MYTIMETABLE]
(
[TIMESTAMP] datetimeoffset(0) NOT NULL,
[VALUE] char(3) NOT NULL
);
GO
Fill the table with some records, in my instance, there will eventually be millions of records, hence why this requirement is a little more complicated.
SET NOCOUNT ON;
GO
WHILE ( ( SELECT COUNT(*) FROM [dbo].[MYTIMETABLE] ) < 1000 )
BEGIN
DECLARE #Script nvarchar(max) =
N'INSERT INTO [dbo].[MYTIMETABLE] ( [TIMESTAMP], [VALUE] )
SELECT DATEADD( ' +
CASE ( FLOOR( ( RAND() * 4 ) + 1 ) )
WHEN 1 THEN N'second'
WHEN 2 THEN N'minute'
WHEN 3 THEN N'hour'
WHEN 4 THEN N'day'
END +
N', ' +
CASE ( FLOOR( RAND() * 2 ) )
WHEN 0 THEN N'-'
WHEN 1 THEN N''
END +
CONVERT( nvarchar, FLOOR( ( RAND() * 100 ) + 1 ) ) +
N', SWITCHOFFSET( SYSDATETIMEOFFSET(), ''' +
CASE ( FLOOR( RAND() * 2 ) )
WHEN 0 THEN N'-'
WHEN 1 THEN N'+'
END +
N'0' +
CONVERT( nvarchar, FLOOR( RAND() * 10 ) ) +
N':00'' ) ), ' +
CONVERT( nvarchar, FLOOR( ( RAND() * 100 ) + 1 ) );
--RAISERROR( #Script , 0, 1 ) WITH NOWAIT;
EXEC sp_executesql #Script;
END
GO
The lookup script I came up with:
DECLARE #DateTime datetimeoffset(0) = SYSDATETIMEOFFSET();
SELECT TOP(1) [Current Time] = #DateTime, [Time Difference] = DATEDIFF( second, [TIMESTAMP], #DateTime ), *
FROM [dbo].[MYTIMETABLE]
ORDER BY ABS( DATEDIFF( second, [TIMESTAMP], #DateTime ) );
My question is, is this the most optimal version of this script? It looks very basic and I am worried that once this goes into production and it's running thousands of lookups per day against millions of records that it's going to suffer performance problems.
The script will be housed in a function so that it can be compiled to further optimise it but any additional performance improvement advice would be highly appreciated.

TSQL - Convert Money to Spanish Text

I have come across a very cool function from another source that translates money data types to English Text. It's great, but I also need to do this in Spanish. I tried to edit the numbers to Spanish words but of course the "rules" of Spanish numbering aren't the same as English. Does anyone have something already for converting a money data type to a Spanish text? Below is the one for English for reference. Or can a Spanish speaking db dev help me out here with modifying this one?
CREATE FUNCTION [dbo].[fnNumberToEnglish](#Money AS money)
RETURNS VARCHAR(1024)
AS
BEGIN
DECLARE #Number as BIGINT
SET #Number = FLOOR(#Money)
DECLARE #Below20 TABLE (ID int identity(0,1), Word varchar(32))
DECLARE #Below100 TABLE (ID int identity(2,1), Word varchar(32))
INSERT #Below20 (Word) VALUES
( 'Zero'), ('One'),( 'Two' ), ( 'Three'),
( 'Four' ), ( 'Five' ), ( 'Six' ), ( 'Seven' ),
( 'Eight'), ( 'Nine'), ( 'Ten'), ( 'Eleven' ),
( 'Twelve' ), ( 'Thirteen' ), ( 'Fourteen'),
( 'Fifteen' ), ('Sixteen' ), ( 'Seventeen'),
('Eighteen' ), ( 'Nineteen' )
INSERT #Below100 VALUES ('Twenty'), ('Thirty'),('Forty'), ('Fifty'),
('Sixty'), ('Seventy'), ('Eighty'), ('Ninety')
DECLARE #English varchar(1024) =
(
SELECT Case
WHEN #Number = 0 THEN ''
WHEN #Number BETWEEN 1 AND 19
THEN (SELECT Word FROM #Below20 WHERE ID=#Number)
WHEN #Number BETWEEN 20 AND 99
-- SQL Server recursive function
THEN (SELECT Word FROM #Below100 WHERE ID=#Number/10)+ '-' +
dbo.fnMoneyToEnglish( #Number % 10)
WHEN #Number BETWEEN 100 AND 999
THEN (dbo.fnMoneyToEnglish( #Number / 100))+' Hundred '+
dbo.fnMoneyToEnglish( #Number % 100)
WHEN #Number BETWEEN 1000 AND 999999
THEN (dbo.fnMoneyToEnglish( #Number / 1000))+' Thousand '+
dbo.fnMoneyToEnglish( #Number % 1000)
WHEN #Number BETWEEN 1000000 AND 999999999
THEN (dbo.fnMoneyToEnglish( #Number / 1000000))+' Million '+
dbo.fnMoneyToEnglish( #Number % 1000000)
ELSE ' INVALID INPUT' END
)
SELECT #English = RTRIM(#English)
SELECT #English = RTRIM(LEFT(#English,len(#English)-1))
WHERE RIGHT(#English,1)='-'
IF ##NestLevel = 1
BEGIN
SELECT #English = #English+' POINT '
SELECT #English = #English+
convert(varchar,convert(int,100*(#Money - #Number)))
END
RETURN (#English)
END
So I just learned Spanish and did it myself. Enjoy!
ALTER FUNCTION [dbo].[fnMoneyToSpanish](#Money AS money)
RETURNS VARCHAR(1024)
AS
BEGIN
DECLARE #Number as BIGINT
SET #Number = FLOOR(#Money)
DECLARE #Below20 TABLE (ID int identity(0,1), Word varchar(32))
DECLARE #Below100 TABLE (ID int identity(2,1), Word varchar(32))
INSERT #Below20 (Word) VALUES
( 'cero'), ('uno'),( 'dos' ), ( 'tres'),
( 'cuatro' ), ( 'cinco' ), ( 'seis' ), ( 'siete' ),
( 'ocho'), ( 'nueve'), ( 'diez'), ( 'once' ),
( 'doce' ), ( 'trece' ), ( 'catorce'),
( 'quince' ), ('dieciséis' ), ( 'diecisiete'),
('dieciocho' ), ( 'diecinueve' )
INSERT #Below100 VALUES ('veinti'), ('treinta'),('cuarenta,'), ('cincuenta'),
('sesenta'), ('setenta'), ('ochenta'), ('coventa')
DECLARE #English varchar(1024) =
(
SELECT Case
WHEN #Number = 0 THEN ''
WHEN #Number BETWEEN 1 AND 19
THEN (SELECT Word FROM #Below20 WHERE ID=#Number)
WHEN #Number BETWEEN 20 AND 99
THEN (SELECT CASE WHEN WORD = 'veinti' AND #Number = '20' THEN 'viente' ELSE WORD END FROM(SELECT Word FROM #Below100 WHERE ID=#Number/10)d) + CASE WHEN ##NestLevel in (3,4) AND (SELECT Word FROM #Below100 WHERE ID=#Number/10) <> 'veinti' THEN ' y ' ELSE '' END + --concat(' Number:',#Number,' Level:', ##NestLevel, ' ') +
dbo.fnMoneyToSpanish( #Number % 10)
WHEN #Number BETWEEN 100 AND 999
THEN CASE WHEN #Number < 200 THEN ' ciento ' ELSE (dbo.fnMoneyToSpanish( #Number / 100)) + 'cientos ' END +
dbo.fnMoneyToSpanish( #Number % 100)
WHEN #Number BETWEEN 1000 AND 999999
THEN CASE WHEN #Number < 2000 THEN ' mil ' ELSE (dbo.fnMoneyToSpanish( #Number / 1000)) + ' mil ' END +
dbo.fnMoneyToSpanish( #Number % 1000)
WHEN #Number BETWEEN 1000000 AND 999999999
THEN CASE WHEN #Number < 200000 THEN ' millón ' ELSE (dbo.fnMoneyToSpanish( #Number / 1000000)) + ' millones ' END +
dbo.fnMoneyToSpanish( #Number % 1000000)
ELSE ' INVALID INPUT' END
)
SELECT #English = RTRIM(#English)
SELECT #English = RTRIM(LEFT(#English,len(#English)-1))
WHERE RIGHT(#English,1)=' y '
IF ##NestLevel = 1
BEGIN
SELECT #English = #English+' dólares y '
SELECT #English = #English+
convert(varchar,convert(int,100*(#Money - #Number))) +' cents'
END
RETURN (ltrim(#English))
END
--select [dbo].[fnMoneyToSpanish](2654876.36)

Loop through a table without using a cursor or temp table finding records over 30 days old

Using SQL Server 2012. I need to loop through my table without using a cursor or a temp table. Also, I know the debate re: cursors... :) AND I have script at the bottom of this question using sys.databases and msdb.dbo.restorehistory getting the last restored, but this doesn't fix my immediate need for this specific table.
Scenario: I have a table that I created for restore tests:
CREATE Table RestoreTests (
RestoreTestID int Not NULL,
ServerName varchar (255) Not NULL,
DatabaseName varchar (255) Not NULL,
RestoreDate datetime Not NULL,
BackupFile varchar (1000) Not NULL)
I inserted 8 records with:
2 servers - 4 records/server,
.bak and .trn for each DatabaseName, and
dates < 30 days and dates > 30 days - 4 records each.
I want to identify all records on my table that are > 30 days.
SELECT * FROM RestoreTests WHERE RestoreDate DATEDIFF(Day,RestoreDate, GETDATE()) > 30
My syntax:
declare #RestoreTestID int
declare #ServerName varchar(255)
declare #DatabaseName varchar(255)
declare #RestoreDate datetime
declare #BackupFile varchar(1000)
declare #id int
set #id = 0
select top 1 #RestoreTestID = RestoreTestID,
#ServerName = ServerName,
#DatabaseName = DatabaseName,
#RestoreDate = RestoreDate,
#BackupFile = BackupFile
from dbo.RestoreTests
where RestoreTestID > #id and DATEDIFF(DAY,RestoreDate, GETDATE()) > 30
order by RestoreTestID asc
while ##ROWCOUNT > 0
begin
print 'loop RestoreTestID=' + cast(#RestoreTestID as varchar(255)) +
', ServerName=' + #ServerName +
', DatabaseName' + #DatabaseName +
', RestoreDate' + CAST(#RestoreDate as varchar(255)) +
', BackupFile' + #BackupFile
set #id = #RestoreTestID
select top 1 #RestoreTestID = RestoreTestID
from dbo.RestoreTests
where RestoreTestID > #id and DATEDIFF(DAY,RestoreDate, GETDATE()) > 30
order by RestoreTestID
end
I'm getting error message:
Msg 137, Level 15, State 1, Line 1
Must declare the scalar variable "#RestoreTestID".
Msg 137, Level 15, State 2, Line 13
Must declare the scalar variable "#RestoreTestID".
Msg 137, Level 15, State 2, Line 19
Must declare the scalar variable "#RestoreTestID".
Msg 137, Level 15, State 1, Line 21
Must declare the scalar variable "#RestoreTestID".
When I was dinking around earlier I did get a result set with all eight records and not the 4 that are over 30 days. Any assistance offered is greatly appreciated!!
Last Restore Syntax:
WITH LastRestores AS
(
SELECT
DatabaseName = [d].[name] ,
[d].[create_date] ,
[d].[compatibility_level] ,
[d].[collation_name] ,
r.*,
RowNum = ROW_NUMBER() OVER (PARTITION BY d.Name ORDER BY r.[restore_date] DESC)
FROM master.sys.databases d
LEFT OUTER JOIN msdb.dbo.[restorehistory] r ON r.[destination_database_name] = d.Name
)
SELECT *
FROM [LastRestores]
WHERE [RowNum] = 1
select * from [dbo].[RestoreTests]
Ok, I figured out what I needed. Here's the syntax I arrived at:
declare #RestoreTestID int
declare #id int
declare #ServerName varchar(255);
declare #DatabaseName varchar (255);
declare #RestoreDate datetime;
declare #BackupFile varchar (1000);
set #id = 0
select top 1
#RestoreTestID = RestoreTestID,
#ServerName = ServerName,
#DatabaseName = DatabaseName,
#RestoreDate = RestoreDate,
#BackupFile = BackupFile
from dbo.RestoreTests
where RestoreTestID > #id and RestoreDate <= DATEADD(day,-31,getdate())
order by RestoreTestID
while ##ROWCOUNT = 1
begin
print 'RestoreTestID = ' + cast(#RestoreTestID as varchar(255)) +
', ServerName = ' + #ServerName +
', Databasename = ' + #DatabaseName +
', RestoreDate = ' + cast(#RestoreDate as varchar (255)) +
', BackupFile = ' + #BackupFile
set #id = #RestoreTestID
select top 1 #RestoreTestID = RestoreTestID
from dbo.RestoreTests
where RestoreTestID > #id and RestoreDate <= DATEADD(day,-31,getdate())
order by RestoreTestID
end

T-SQL to check and update

Getting error:
Msg 512, Level 16, State 1, Line 48
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Msg 512, Level 16, State 1, Line 87
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Need to check each record in the table and update a table if there is no records = to 0,00. Then update an existing table with current values. I want to be able to track current data and compare with old data.
DECLARE #LocTime DATETIME;
DECLARE #MinDateTime DATETIME;
DECLARE #SystemDateTime DATETIME;
DECLARE #LIR MONEY;
DECLARE #LOAR MONEY;
SELECT #SystemDateTime = SYSDATETIME(); --= '2016-12-07 23:30:00'
SELECT #MinDateTime = DATEADD(mi,-30,#SystemDateTime) --go back half hour of reads, job is running every 10 minutes for overlap
--select #MinDateTime, #SystemDateTime
IF OBJECT_ID(N'tempdb..##LastOver2') IS NOT NULL
BEGIN
DROP TABLE ##LastOver2
END
--make temp table to hold found data
CREATE TABLE ##LastOver2 (ReadDate DATETIME
, FacilityName NVARCHAR(100)
, FacilityID UNIQUEIDENTIFIER
, LastInstantRead MONEY
, LastOverAllRead MONEY
, FacilityTimeZone INT
, FacilityTime DATETIME)
INSERT INTO ##LastOver2 (ReadDate, FacilityName
, FacilityID, LastInstantRead
, LastOverAllRead
, FacilityTimeZone, FacilityTime)
SELECT DISTINCT --why distinct?
fmr.ReadDate, f.Name
, f.FacilityID, fm.LastInstantRead
, fm.LastOverAllRead
, f.Timezone
, #LocTime
FROM [dbo].[Facilities] f WITH (NOLOCK)
JOIN [dbo].[FacilityMeters] fm WITH (NOLOCK)
ON F.FacilityID = FM.FacilityID
JOIN FacilityMeterReadings fmr WITH (NOLOCK)
ON FM.FacilityMeterID = FMR.FacilityMeterID
WHERE --fm.FacilityMeterID = '9268d1af-cc29-432c-9cdb-06c158180d2f'
(fmr.ReadDate >= #MinDateTime and ReadDate <= #SystemDateTime) --including on both side to continue with overlap
and (fm.IsVirtual = 0 and fm.ParentMeterID is NULL)
--and (fm.LastInstantRead = 0.00 and fm.LastOverAllRead = 0.00)
AND f.SuppressMonitoring = 0
select * from ##LastOver2
IF (select LastInstantRead from ##LastOver2) = 0.00 OR (SELECT LastOverAllRead FROM ##LastOver2) = 0.00
BEGIN
--UPDATE dbo.Facilities
--SET SuppressMonitoring = 1
-- FROM dbo.Facilities F
-- JOIN ##LastOver L
-- ON F.FacilityID = l.FacilityID
-- WHERE F.FacilityID = l.FacilityID
DECLARE #body_content NVARCHAR(150);
DECLARE #StartHour NVARCHAR(8) = '08:30:00' ;
DECLARE #StopHour NVARCHAR(8) = '16:00:00';
--why distinct, is it already distinct. Is is supposed to distinct the facility or meter?
DECLARE #SQLScript NVARCHAR(200) = 'SELECT distinct * FROM ##LastOver2 WHERE CONVERT(TIME, FacilityTime) BETWEEN '
+ CHAR(39) + #StartHour + CHAR(39) +' AND ' + CHAR(39) + #StopHour + CHAR(39);
--only looking for reads during day hours? shouldn't use between.
DECLARE #copyRecipients NVARCHAR(100)
SET #body_content = 'Please check the attached file. This was from server: ' + ##SERVERNAME
DECLARE #fileName nvarchar(255);
select #fileName = 'BadReading_' + replace(replace(convert(nvarchar(19),getdate(), 126),':',''),'-','') + '.txt';
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'SQLSupport'
,#recipients = 'Btest#test.com'
--, #recipients = 'jira#cleanenergycollective.atlassian.net'
--, #copy_recipients= #copyRecipients
--, #copy_recipients= 'Bill.Lugaila#easycleanenergy.com;yvonne.lewis#easycleanenergy.com;don.munroe#easycleanenergy.com;Justin.Reed#easycleanenergy.com'
, #query = #SQLScript
, #subject = 'Facility Meter Check and Updating table' --change so easier to see in emails
, #body= #body_content
, #importance= 'High'
, #attach_query_result_as_file = 1
, #query_attachment_filename = #fileName ;
--select #SQLScript
END
ELSE IF (select LastInstantRead from ##LastOver2) != 0.00 OR (SELECT LastOverAllRead FROM ##LastOver2) != 0.00
BEGIN
UPDATE [dbo].[_LastInstant_OverAll_Read]
SET [FacilityName] = lo.FacilityName,
[LastInstantRead] = lo.LastInstantRead,
[LastOverAllRead]= lo.LastOverAllRead,
[Time_Date] = (SELECT CONVERT(DateTime, SysDateTime()))
FROM ##LastOver2 lo
WHERE lo.FacilityName = [dbo].[_LastInstant_OverAll_Read].FacilityName
AND lo.LastInstantRead != [dbo].[_LastInstant_OverAll_Read].LastInstantRead
AND lo.LastOverAllRead != [dbo].[_LastInstant_OverAll_Read].LastOverAllRead
END
the following could be returning multiple rows:
.
.
.
IF
(
SELECT LastInstantRead
FROM ##LastOver2
) = 0.00
OR
(
SELECT LastOverAllRead
FROM ##LastOver2
) = 0.00
.
.
.
IF
(
SELECT LastInstantRead
FROM ##LastOver2
) != 0.00
OR
(
SELECT LastOverAllRead
FROM ##LastOver2
) != 0.00
if you are expecting them to return only 1 row then you will need to fix the issue with the following query:
SELECT DISTINCT --why distinct?
fmr.ReadDate,
f.Name,
f.FacilityID,
fm.LastInstantRead,
fm.LastOverAllRead,
f.Timezone,
#LocTime
FROM [dbo].[Facilities] f WITH (NOLOCK)
JOIN [dbo].[FacilityMeters] fm WITH (NOLOCK) ON F.FacilityID = FM.FacilityID
JOIN FacilityMeterReadings fmr WITH (NOLOCK) ON FM.FacilityMeterID = FMR.FacilityMeterID
WHERE --fm.FacilityMeterID = '9268d1af-cc29-432c-9cdb-06c158180d2f'
(fmr.ReadDate >= #MinDateTime
AND ReadDate <= #SystemDateTime) --including on both side to continue with overlap
AND (fm.IsVirtual = 0
AND fm.ParentMeterID IS NULL)
--and (fm.LastInstantRead = 0.00 and fm.LastOverAllRead = 0.00)
AND f.SuppressMonitoring = 0;

Inherit values from previous records

I have the following table where all fields except Value as a part of a unique index on the table. I'd like to inherit the Value from a record with Value<>NULL to the next revision record only if that record has a NULL value (See example below):
Var[n]Value fields ate varchar and there maybe 1-n number of Var[n]value fields in my table.
Source:
Document# Revision Project# config# Var1Value Var2Value
1744 1 2 1 NULL NULL
1744 2 2 1 NULL NULL
1744 3 2 1 Tit1 ABC
1744 4 2 1 Tit2 ABD
1744 5 2 1 NULL NULL
1744 6 2 1 NULL SDC
1744 7 2 1 AS
1744 8 2 1 Tit3 NULL
Needed result (notice change for revision records 5 and 6 var1value and 5,8 for var2value):
Document# Revision Project# config# Var1Value Var2Value
1744 1 2 1 NULL NULL
1744 2 2 1 NULL NULL
1744 3 2 1 Tit1 ABC
1744 4 2 1 Tit2 ABD
1744 5 2 1 Tit2 ABD
1744 6 2 1 Tit2 SDC
1744 7 2 1 AS
1744 8 2 1 Tit3 AS
Any idea how to handle it by SQL?
Please advise.
I tried the following:
declare #TableName as VarChar(32) = 'MYTABLE'
declare #SetClause as VarChar(1024)
declare #LWhereClause as VarChar(1024)
declare #RWhereClause as VarChar(1024)
-- Get the column names.
select Column_Name
into #Columns
from Information_Schema.Columns
where Table_Name = #TableName and Column_Name like 'Var%'
--select * from #Columns
-- Assemble the clauses we'll need for the UPDATE statement.
declare #ColumnName as VarChar(32)
while ( ##RowCount > 0 )
begin
select top 1 #ColumnName = Column_Name
from #Columns
order by Column_Name
set #SetClause = case when #SetClause is NULL then '' else #SetClause + ', ' end +
#ColumnName + ' = Coalesce( L.' + #ColumnName + ', R.' + #ColumnName + ' )'
set #LWhereClause = case when #LWhereClause is NULL then '' else #LWhereClause + ' or ' end +
'L.' + #ColumnName + ' is NULL'
set #RWhereClause = case when #RWhereClause is NULL then '' else #RWhereClause + ' or ' end +
'R.' + #ColumnName + ' is not NULL'
delete from #Columns
where Column_Name = #ColumnName
end
--select #SetClause, #LWhereClause, #RWhereClause
-- Put together the UPDATE statement.
declare #Update as nVarChar(max)
set #Update=''
set #Update=#Update +
'update L set ' + #SetClause + ' from ' + #TableName +
' as L inner join ' + #TableName + ' as R on R.DocId = L.DocId and R.Rev = L.Rev - 1 and R.Proj = L.Proj and R.Conf = L.Conf' +
' where ( ' + #LWhereClause + ' ) and ( ' + #RWhereClause + ' )'
-- Put together the entire loop. This needs work.
declare #Loop as nVarChar(max)
set #Loop =''
set #Loop=#Loop+
'#declare Eleanor as Int = 42;
while ( #Eleanor > 0 ) '
+ #Update + '
set #Eleanor = ##RowCount
end'
--select #Loop
-- Execute it.
exec #Loop
drop table #Columns
and I get the following error on exec loop. Why it is truncating the nvarchar string?
Msg 203, Level 16, State 2, Line 53
The name '#declare Eleanor as Int = 42;
while ( #Eleanor > 0 ) update L set variable104 = Coalesce( L.variable104, R.variable104 ), variable105 = Coalesce( L.variable105, R.variable105 ), variable106 = Coalesce( L.variable106, R.variable106 ), variable107 = Coalesce( L.variable107, R.variable107 ), variable112 = Coalesce( L.variable112, R.variable112 ), variable116 = Coalesce( L.variable116, R.variable116 ), variable119 = Coalesce( L.variable119, R.variable119 ), variable120 = Coalesce( L.variable120, R.variable120 ), variable121 = Coalesce( L.variable121, R.variable121 ), variable122 = Coalesce( L.variable122, R.variable122 ), variable124 = Co' is not a valid identifier.
exact duplicate of :
SQL QUERY replace NULL value in a row with a value from the previous known value
p.s:i don't have privilege to comment.so i wrote as answer.
EDIT: Begging your pardon, but I failed to consider the variable columns.
This query will retrieve the columns defined in YourTable:
select Column_Name from Information_Schema.Columns where Table_Name = 'YourTable'
Thereafter, you need to build a dynamic query and EXEC it. You could craft code like that shown below for each individual column, or to process all of the columns in one go.
Building the query dynamically is a somewhat tedious process. The following should get you well on your way.
declare #TableName as VarChar(32) = 'YourTable'
declare #SetClause as VarChar(1024)
declare #LWhereClause as VarChar(1024)
declare #RWhereClause as VarChar(1024)
-- Get the column names.
select Column_Name
into #Columns
from Information_Schema.Columns
where Table_Name = #TableName and Column_Name like 'Var%'
select * from #Columns
-- Assemble the clauses we'll need for the UPDATE statement.
declare #ColumnName as VarChar(32)
while ( ##RowCount > 0 )
begin
select top 1 #ColumnName = Column_Name
from #Columns
order by Column_Name
set #SetClause = case when #SetClause is NULL then '' else #SetClause + ', ' end +
#ColumnName + ' = Coalesce( L.' + #ColumnName + ', R.' + #ColumnName + ' )'
set #LWhereClause = case when #LWhereClause is NULL then '' else #LWhereClause + ' or ' end +
'L.' + #ColumnName + ' is NULL'
set #RWhereClause = case when #RWhereClause is NULL then '' else #RWhereClause + ' or ' end +
'R.' + #ColumnName + ' is not NULL'
delete from #Columns
where Column_Name = #ColumnName
end
select #SetClause, #LWhereClause, #RWhereClause
-- Put together the UPDATE statement.
declare #Update as VarChar(4096) =
'update L set ' + #SetClause + ' from ' + #TableName +
' as L inner join ' + #TableName + ' as R on R.DocId = L.DocId and R.Rev = L.Rev - 1 and R.Proj = L.Proj and R.Conf = L.Conf' +
' where ( ' + #LWhereClause + ' ) and ( ' + #RWhereClause + ' )'
-- Put together the entire loop. This needs work.
declare #Loop as VarChar(4096) =
'#declare Eleanor as Int = 42; ...' + #Update + '...'
select #Loop
-- Execute it.
exec #Loop
drop table #Columns
Here's one dreadful way that is based on revision numbers being dense:
declare #Docs as Table ( DocId Int, Rev Int, Proj Int, Conf Int, Var1 VarChar(10) Null, Var2 VarChar(10) Null )
insert into #Docs ( DocId, Rev, Proj, Conf, Var1, Var2 ) values
( 1744, 1, 2, 1, NULL, NULL ),
( 1744, 2, 2, 1, NULL, NULL ),
( 1744, 3, 2, 1, 'Tit1', 'ABC' ),
( 1744, 4, 2, 1, 'Tit2', 'ABD' ),
( 1744, 5, 2, 1, NULL, NULL ),
( 1744, 6, 2, 1, NULL, 'SDC' ),
( 1744, 7, 2, 1, '', 'AS' ), -- The example data for this row is unclear.
( 1744, 8, 2, 1, 'Tit3', NULL )
select * from #Docs
declare #Eleanor as Int = 42
while ( #Eleanor > 0 )
begin
update L
set Var1 = Coalesce( L.Var1, R.Var1 ), Var2 = Coalesce( L.Var2, R.Var2 )
from #Docs as L inner join
#Docs as R on R.DocId = L.DocId and R.Rev = L.Rev - 1 and R.Proj = L.Proj and R.Conf = L.Conf
where ( L.Var1 is NULL or L.Var2 is NULL ) and ( R.Var1 is not NULL or R.Var2 is not NULL )
set #Eleanor = ##RowCount
end
select * from #Docs