Conversion of Plsql function to support snowflake - plsql

I need some help here to convert the below oracle function to Snowflake sql. The requirement here is the below code converts the local currency amount to USD for with the specific rate at the particular date.
Condition here is we dont have dates below 2017, so whatever the contracts created should pick the minimum value from that particular table for that respective currency, and if there are any future contracts which is more than sysdate, then it shoudl pick latest (max rate) dates rate for that currency.
Please help me here.
create or replace
FUNCTION get_exchange_rate(
p_rate_amount IN NUMBER,
p_exchange_source IN VARCHAR2,
p_exchange_target IN VARCHAR2,
p_date IN DATE)
RETURN NUMBER
is
l_rate number := null;
BEGIN
BEGIN
SELECT exchange_rate
INTO l_rate
FROM exchange_rate_table
WHERE exchange_period='Daily'
AND exchange_target =p_exchange_target
AND exchange_source =p_exchange_source
AND begin_effect_date=p_date;
RETURN l_rate*p_rate_amount;
EXCEPTION
WHEN no_data_found THEN
SELECT exchange_rate
INTO l_rate
FROM exchange_rate_table
WHERE exchange_period='Daily'
AND exchange_target =p_exchange_target
AND exchange_source =p_exchange_source
AND begin_effect_date=
(SELECT MIN(begin_effect_date)
FROM exchange_rate_table
where 1 = 1
and exchange_source = p_exchange_source
AND exchange_target =p_exchange_target
);
RETURN l_rate*p_rate_amount;
WHEN OTHERS THEN
return null;
END;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END get_exchange_rate;
Sample Data:
Desired result :
Explanation :
In exchange table we have all the conversion rates from all source currencies to target currencies for all the exchange periods like Daily, Monthly,Annual.
Here in my scenario, All the source currencies should convert to target currencies as USD for that respective dates(daily rates) . like matching dates, with matching source currency codes.
When we dont have currency rates for particular local currency to USD :
Here in this case, exchange dim table has started from 01-10-2021, where contracted started on 01-09-2021, then it should pick minimum date from exchange table, (lets say in case of EUR currency.) And at the same time, we may have contracts which starts from future dates also, in that case we should pick maximum date currency rate. (eg CAD currency)
My code in snowflake:
[![enter image description here][3]][3]

Related

Reading 'bad' Sqlite data with FireDAC

I just learned that the Sqlite Manager plug-in for Firefox will go away in November, so I've been trying to recreate its functionality in Delphi: open Sqlite databases, enter SQL queries. I'm running Tokyo.
My problem comes with Sqlite fields defined as 'date.' While Sqlite allows specification of data types, it allows pretty much anything to be put in any field. FireDAC handles bad entries in integer or float fields ('bar' becomes 0, '32foo' becomes 32), but it hiccups on fields described as 'date.'
For example, I have a table:
CREATE TABLE "someTable" ("id" INTEGER, "s" text(10), "d" date)
With this data:
INSERT INTO "someTable" VALUES ("1","good date","2017-09-09");
INSERT INTO "someTable" VALUES ("2","bad date","2017-09-0b");
INSERT INTO "someTable" VALUES ("3","empty d","");
INSERT INTO "someTable" VALUES ("4","null date",null);
Opening FDQuery q1 with SQL = "select * from someTable", a bad date (such as the second record) raises an EConvertError ("Invalid argument to date encode"). I tried to get around it by adding a maprule (q1 is a FDQuery):
with q1.FormatOptions do begin
OwnMapRules := True;
with MapRules.Add do begin
SourceDataType := dtDate;
TargetDataType := dtAnsiString;
sizemin := 10;
sizemax := 256;
PrecMin := -1;
PrecMax := -1;
ScaleMin := -1;
ScaleMax := -1;
end;
end;
However, that raises an EFDException:
[FireDAC][DatS]-32. Variable length column overflow. Value length - [10], column maximum length - [0].
What am I missing?
Why do I get "Invalid argument to date encode" exception when fetching tuple containing invalid DATE type field value?
The problem is that FireDAC expects DATE data type values represented as a string in fixed format YYYY-MM-DD where all members must be integers. Internally used FDStr2Int function doesn't use any detection of invalid input and works directly with ASCII ordinary values shifted by the 0 char, so input like e.g. GHIJ-KL-MN results in year 25676, month 298, day 320 after parsing. And just these misinterpreted values are then passed to the EncodeDate function, which fails for such values with the exception you've mentioned:
Invalid argument to date encode
The above happens inside the GetData method (ParseDate nested function). One possible way for fixing this issue on FireDAC's side could be using safer function TryEncodeDate instead of a direct encoding attempt. Similar problem is with TIME data type string value.

pl/sql need help undestanding

trying to get total sales for a specific zip; I have done a couple of these, but cant seem to get the total and I'm missing something.
Tables are Customers that has the zip,
sales that has the gross sale amount.
This one executes complete but no total; so what am I missing?
Also for the sake of asking how could I modify this to ask me in an interface, where I can input the zip? I started that too but; again, I get the question when I insert the zip it gives me errors.
SET SERVEROUTPUT ON
DECLARE
V_SALES NUMBER (10,2);
V_ZIP VARCHAR2(5) NOT NULL := 48228;
BEGIN
SELECT SUM(S.GROSS_SALE_PRICE) -- GROUP FUNCTION
INTO V_SALES
FROM SALES S
WHERE CUST_ID = V_ZIP;
DBMS_OUTPUT.PUT_LINE ('TOTAL SALES FOR ZIP 48228, IS'|| TO_NUMBER(V_SALES));
END;
/
Try including a group by clause
SET SERVEROUTPUT ON
DECLARE
V_SALES NUMBER (10,2);
V_ZIP VARCHAR2(5) NOT NULL := 48228;
BEGIN
SELECT SUM(S.GROSS_SALE_PRICE) -- GROUP FUNCTION
INTO V_SALES
FROM SALES S
WHERE CUST_ID = V_ZIP
GROUP BY CUST_ID;
DBMS_OUTPUT.PUT_LINE ('TOTAL SALES FOR ZIP 48228, IS'|| TO_NUMBER(V_SALES));
END;

Lazarus SQLite date entry displays improperly

UPDATE: Issue was solved by #whosrdaddy. See comments below this question.
I am trying to resolve the following peculiar case: In a friend's Lazarus project, he tries to query an entry in SQLite. The asString()-method (in the procedure for displaying appointments) returns the proper date on Windows 64 Bit. On a 32 Bit operating system, however, only the first two digits are displayed ('16' instead of '28.02.2016'). What could be the reason?
This is the source code for initialising the form:
// Initialise Form
procedure TForm1.FormCreate(Sender: TObject);
begin
SQLite3Connection1.DatabaseName:='Kalender.sqlite';
SQLTransaction1.Database:=SQLite3Connection1;
SQLQuery1.Transaction:=SQLTransaction1;
// Create Table "tblTermine"
SQLQuery1.SQL.text := 'CREATE TABLE IF NOT EXISTS tblKalender (Datum DATETIME, Termin VARCHAR(10))';
SQLQuery1.ExecSQL;
SQLTransaction1.commit;
end;
There are two further procedures:
// Display Appointments
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Clear;
SQLQuery1.Close;
SQLQuery1.SQL.text:='SELECT * FROM tblKalender';
SQLQuery1.Open;
while not SQLQuery1.Eof do
begin
// Should return 'dd.mm.yyyy'
ListBox1.Items.add(SQLQuery1.Fields[0].AsString+ ': ' + SQLQuery1.Fields[1].AsString);
SQLQuery1.Next;
end;
end;
// Save Appointment
procedure TForm1.Button2Click(Sender: TObject);
var Termin: string;
Datum: TDate;
begin
Termin:=Edit1.text;
if calendardialog1.execute then
Datum:=TDate(calendardialog1.date);
SQLQUERY1.close;
SqlQuery1.SQL.text:= 'Insert into tblKalender Values (:Datum, :Termin)';
SqlQuery1.ParamByName('Datum').AsDate:= Datum;
SqlQuery1.ParamByName('Termin').AsString:= Termin;
SqlQuery1.ExecSQL;
SqlTransaction1.Commit;
Button1.Click;
Edit1.Text := '';
end;
The intended output into the TListBox would be something like this.
you should convert first the DateTime to a Julian Date
function DateTimeToJulianDate(const Datum: TDateTime): Double;
and
SqlQuery1.SQL.text:= 'Insert into tblKalender Values (:Datum, :Termin)';
SqlQuery1.ParamByName('Datum').AsFloat := DateTimeToJulianDate(Datum);
...
SqlQuery1.ExecSQL;
to test and get the value use :
function TryJulianDateToDateTime(const AValue: Double; ADateTime: TDateTime):Boolean;
if TryJulianDateToDateTime(SQLQuery1.Fields[0].AsFloat,myDate)
then
ListBox1.Items.add(DateTimeToStr(myDate)+ ': ' + .....
else
ShowMessage('Not a valid Julian date');
Update
SQLite are capable of storing dates and times as TEXT, REAL, or INTEGER values:
TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS").
REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic
Gregorian calendar.
INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.
Double Test
procedure TForm1.Button3Click(Sender: TObject);
var
Datum : TDate;
myDate : TDateTime;
JulianDouble : Double;
begin
// uses ....,DateUtils
Datum := StrToDate('01.01.2013'); //German Culture settings
Memo1.Lines.Add('01/01/2013 = '+DateTimeToStr(Datum)+ ' TDate as Text');
Memo1.Lines.Add('01/01/2013 = '+FloatToStr(Datum) + ' TDate Double');
JulianDouble := DateTimeToJulianDate(Datum);
Memo1.Lines.Add('01/01/2013 = '+FloatToStr(JulianDouble) + ' Julian Double');
if TryJulianDateToDateTime(JulianDouble,myDate)
then
Memo1.Lines.Add('01/01/2013 = '+DateTimeToStr(myDate)+ ' TDate as Text')
else
ShowMessage('Not a valid Julian date');
end;
Output :
01/01/2013 = 01.01.2013 TDate as Text
01/01/2013 = 41275 TDate Double
01/01/2013 = 2456293,5 Julian Double
01/01/2013 = 01.01.2013 TDate as Text
Update-2 :
To write Delphi TDate Double to a SQLite Date field is wrong
Your comment shows me that you do not know the problems.
Of course you can directly write a Delphi Double value into a database field. And read it back to a TDateTime.
This will quickly lead to problems.
Examples:
SQLite:
These functions only work for dates between 0000-01-01 00:00:00 and
9999-12-31 23:59:59 (julidan day numbers 1721059.5 through 5373484.5).
For dates outside that range, the results of these functions are
undefined.
41275 Delphi TDate Double for 2013/01/01 is outside of above range !!
SQLite's own date functions can no longer be used.
Compute the current date.
SELECT date('now');
Compute the last day of the current month.
SELECT date('now','start of month','+1 month','-1 day');
Compute the date and time given a unix timestamp 1092941466.
SELECT datetime(1092941466, 'unixepoch');
Compute the date and time given a unix timestamp 1092941466, and compensate for your local timezone
SELECT datetime(1092941466, 'unixepoch', 'localtime');
Compute the number of days since the signing of the US Declaration of Independence.
SELECT julianday('now') - julianday('1776-07-04');
etc. etc.
Changing the date value with above functions will give you a double 2456293,5 for a Date 2013/01/01
If you now use unproved and pass it to a Delphi TDateTime it will be 3387/11/26).
This is not far from the maximum value of a TDateTimePicker.
which is 2958465.5 and means 9999/12/31
DateTimePicker1.DateTime := 2958465.5;
DateTimePicker1 9999/12/31
If one already know that it is wrong one should not use it up to a crash.
SQLite uses something called manifest typing which means that if you store values of (Delphi) type TDateTime, SQLite will store the underlying floating point value without warning or message, and offer you the value back so you won't notice that SQLite doesn't treat this as a date-time-value, unless you're trying to manipulate the value from SQL.
To counteract this, I use this bit of SQL code to convert Delphi TDateTime values to an actual SQL datetime:
datetime('1900-01-01','+'||(myDateField-2)||' day')
(see also here)

SQL Server Filtering by DateTime column, when TIME portion is provided sometimes

In an SSRS report, the user searches based on start date and end date.
The challenge is, as I discovered recently, he sometimes, not always, provides the time component while searching.
Currently, the filter is done like this:
if #pEndDate is null
SET #pEndDate = getdate()
SET #PEndDate = DateAdd(dd,1,#PEndDate)
SELECT ........
FROM .....
WHERE ( Createdon >= #PStartDate AND Createdon < #PEndDate)
This is fine when he searches without time (example - #PStartDate = 2/23/2015 and #PEndDate = 2/24/2015)
How should I structure the query to deal with the time portion when he provides it? (example - #PStartDate = 2/23/2015 15:00 and #PEndDate = 2/24/2015 15:00)
If this is answered elsewhere, please point me to it. Thank you.
If you just want to match the date part then there are lot options.
1) You can use the Date type for the parameter PEndDate and PStartDate to nullify the time part
2) You can use the Convert method to get only date part of the parameter while matching.CONVERT (DATE, #PEndDate) OR CONVERT(varchar,#PEndDate,103)
3) Get Date Part only from DateTime using DateTime functions
ATEADD(dd, 0,
DATEDIFF(dd, 0, #PEndDate))
4) Get Date Part only from DateTime using FLOOR and CAST functions
CAST( -- Convert the integer to DATE
FLOOR(-- Get largest Integer less than or equal to the decimal value
CAST(GETDATE() AS DECIMAL(12, 5)) -- Convert DATETIME to DECIMAL)
AS DATETIME) 'Date Part Only'
5) Get Date Part only from DateTime using DATEPART and CONVERT functions
CONVERT(VARCHAR(4),DATEPART(YEAR, #GETDATE))
+ '/'+ CONVERT(VARCHAR(2),DATEPART(MONTH, #GETDATE))
+ '/' + CONVERT(VARCHAR(2),DATEPART(DAY, #GETDATE))
'Date Part Only'
Use whichever method suits you and you find fancy.
UPDATE
As you mentioned you need to get the time part to 00:00 with date so you can try as,
SELECT CAST( convert(varchar(10),GETDATE(),112) AS DATETIME)
--This will give you 2015-02-27 00:00:00.000
SELECT DATEADD(ms,-3, DATEADD(day, DATEDIFF(day,0,GETDATE())+1,0))
--This will give you end of days time 2015-02-27 23:59:59.997
SELECT CONVERT(nvarchar,getdate(),103) + ' 12:59:59 PM'
--This will give you custom time 27/02/2015 12:59:59 PM

Display grand total as the sum of averages (custom subtotal)

I have SQL Server query, which returns the following data:
I want to display the data in RDLC 2008 Report using matrix format which should give the following result:
The Grand Total of Qty field should return 12 for January & 14 for February.
I have tried many different methods one of which is by using the following expression in the matrix 'Qty' textbox :
=IIF(InScope("RowGroup_Category")
,IIF(InScope("RowGroup_SubCategory")
,Fields!Qty.Value
,Code.GetAverageMemberCount(Cint(Avg(Fields!Qty.Value)))
)
,Code.TotalMemberCount
)
The above functions are written in Report Properties Code as below:
Public Dim TotalMemberCount As Integer = 0
Function GetAverageMemberCount(ByVal AverageMemberCount As Integer) As Integer
TotalMemberCount = TotalMemberCount + AverageMemberCount
Return AverageMemberCount
End Function
I have also tried RunningValue(Fields!Qty.Value,Sum,"RowGroup_Category") and many such functions but I am unable to get the exact results. any help would be appreciated.. Thank you
Try adding this into a new column as a test:
RunningValue(Avg(Fields!MemberCount.Value,"RowGroup_Category"),SUM,Nothing)
If the value is correct you should be able to change SUM into MAX when setting this expression in the grand total field.
You can refer to the total like code.TotalMemberCount instead of using a get function
but i don't think you need this function in this case.
Check the following blog for a simular variable referencing situation
The only solution I could find that worked for me to solve this is to calculate the averages in a different dataset and use lookup functions to fill the grand total from there.
In your case I would add a key column in your original dataset:
select Category + '|' + Month as key, Category, SubCategory, Month, Qty, Amt
from YourTable
Create another dataset using:
select Category + '|' + Month as key, Category, Month, avg(Qty)
from YourTable
group by Category, Month
Add the second result as DataSet2 to the report. (In Visual Studio in the Report Data pane right click on DataSets.)
Add to the Report Properties -> Code section the following:
Function SumArray(varArray as array) as Decimal
Dim retVal As Decimal = 0
For Each item As Decimal In varArray
retVal = retVal + item
Next
Return retVal
End Function
Finally in the report use the following expression for Grand Total under Qty:
=code.SumArray(Lookupset(Fields!key.Value, Fields!key.Value, Fields!qty.Value, "DataSet2"))
P.S.: Make sure that the second dataset is also filled by your code the same way the original was.

Resources