Snowflake Data Masking 'Date' datatype - snowflake-cloud-data-platform

I am trying to mask date using:
CREATE OR REPLACE MASKING POLICY date_mask_ldm AS (val date) RETURNS date ->
CASE
WHEN IS_ROLE_IN_SESSION('mask_policy_allowed_ldm') THEN val
ELSE regexp_replace(val,'.+\#', '####')
END;
I cannot find anything anywhere online which helps me with this...can anyone help please?

I don't know what is the expected output but if the column is DATE, then you need to return a valid DATE. So maybe you can truncate the dates up to the years.
CREATE OR REPLACE MASKING POLICY date_mask_ldm AS (val date)
RETURNS date -> CASE WHEN IS_ROLE_IN_SESSION('mask_policy_allowed_ldm')
THEN val
ELSE DATE_TRUNC( years, val ) END;
So if the date is '2022-05-31', it will be shown as '2022-01-01' because of the masking policy.

Related

Date Calculation using DateAdd function not calculating correct date

I need to calculate six months based on below condition :
If EPC Review Date is present then Six Month = EPC ReviewDate + 6M (disregard all other dates)
If MDS and modified both present and MDS Review Date > Modified, then Six Month = MDS review date + 6M
if MDS and Modified both present and Modified > MDS, then six months = Modified + 6M
I wrote the below code
,SIXMONTH = CASE WHEN (EPC.REVIEWDATE IS NOT NULL OR EPC.REVIEWDATE <> '') THEN
CONVERT(VARCHAR(10),DATEADD(MM,6,EPC.REVIEWDATE),101)
WHEN ((EPC.REVIEWDATE IS NULL OR EPC.REVIEWDATE = '') AND (MODI.MODIReviewDate IS
NULL OR MODI.MODIReviewDate = '')) THEN
CONVERT(VARCHAR(10),DATEADD(MM,6,MDS.MDSReviewDate),101)
WHEN ((EPC.REVIEWDATE IS NULL OR EPC.REVIEWDATE = '') AND MDS.MDSReviewDate >
MODI.MODIReviewDate) THEN
CONVERT(VARCHAR(10),DATEADD(MM,6,MDS.MDSReviewDate),101)
WHEN ((EPC.REVIEWDATE IS NULL OR EPC.REVIEWDATE = '') AND (MDS.MDSReviewDate <
MODI.MODIReviewDate)) THEN
CONVERT(VARCHAR(10),DATEADD(MM,6,MODI.MODIReviewDate),101)
END
But my conditions are not working correctly, and it's not calculating date correctly. If data is like below
EPC_revire Date MDS_revieDate Modi_reviewDate SixMonth
NULL 04/27/2022 09/01/2021 03/01/2022 -- it should add 6 months to MDS
Null 11/10/2021 06/23/2022 05/10/2022 -- it should add 6 month to Modi
10/25/2021 07/21/2021 null 04/25/2022 -- it correctly added 6 months to EPC
Can anyone help, please.
Give this a try:
SIXMONTH = CASE
WHEN EPC.REVIEWDATE IS NOT NULL THEN dateadd(month, 6, EPC.REVIEWDATE)
WHEN MODI.MODIReviewDate > MDS.MDSReviewDate THEN dateadd(month, 6, MODI.MODIReviewDate)
WHEN MODI.MODIReviewDate < MDS.MDSReviewDate THEN dateadd(month, 6, MDS.MDSReviewDate)
END
I leave you to figure out what to do when MODI.MODIReviewDate = MDS.MDSReviewDate. If either of those dates (MODIReviewDate, MDSReviewDate) is null, the comparison will return UNKNOWN and fall through to the next branch (if present). This is why you don't need to keep repeating various comparisons.
Note that you save a TRIVIAL amount of typing using the datepart abbreviation; using the complete datepart name makes the code much easier to read and understand. A little effort at formatting your code also helps.
I also ignored the converting to varchar(10). Formatting belongs in the presentation layer.

Creating function in oracle

I am trying to create a function that change the day of a football league; if the match is fixed on saturday then the function update the match day to be the previuos friday, and if the match is fixed on sunday the function update the match date to be on monday. Also the function will show how many rows have been update.
The table I use is as follow:
CREATE TABLE "183400_Matches_Details" (
"183400_Stadiums_id" INTEGER NOT NULL,
"183400_Teams_id" INTEGER NOT NULL,
"183400_Teams_id1" INTEGER NOT NULL,
"183400:Referees_id" INTEGER NOT NULL,
"183400_Matches_number" INTEGER NOT NULL,
"date" DATE NOT NULL,
result VARCHAR2(5) NOT NULL
);
I tried the following statements to build the function, but it always gives me an error:
create or replace function updateDay (
v_number "183400_Matches_Details"."183400_Matches_number"%type)
return date
as
v_fecha "183400_Matches_Details"."date"%type;
begin
SELECT TO_CHAR("date", 'DAY', 'NLS_DATE_LANGUAGE=ENGLISH') as day1 into v_fecha FROM
"183400_Matches_Details"
where "183400_Matches_number" = v_number;
if day1 = 'SATURDAY' then
update "183400_Matches_Details"
set "date" = "date"-1
where "183400_Matches_number" = v_number;
elsif day1 = 'SUNDAY' then
update "183400_Matches_Details"
set "date" = "date"+1
where "183400_Matches_number" = v_number;
end if;
return SQL%ROWCOUNT;
end;
/
select * from "183400_Matches_Details"
DECLARE
v_number "183400_Matches_Details"."183400_Matches_number"%type := &number;
v_total_filas number(8);
BEGIN
v_total_filas := actualizaPrecioCoche(v_number);
DBMS_OUTPUT.put_line('There are ' || v_total_filas || ' updated rows');
END;
/
Any ideas to make it run correctly?=)
I changed your function as it should be. Try below.
CREATE TABLE "183400_Matches_Details"
(
"183400_Stadiums_id" INTEGER NOT NULL,
"183400_Teams_id" INTEGER NOT NULL,
"183400_Teams_id1" INTEGER NOT NULL,
"183400:Referees_id" INTEGER NOT NULL,
"183400_Matches_number" INTEGER NOT NULL,
"datee" DATE NOT NULL,
RESULT VARCHAR2 (5) NOT NULL
);
CREATE OR REPLACE FUNCTION updateDay (
v_number "183400_Matches_Details"."183400_Matches_number"%TYPE)
RETURN DATE
AS
v_fecha "183400_Matches_Details"."datee"%TYPE;
sql_qry VARCHAR2 (400 CHAR);
BEGIN
sql_qry :=
'SELECT TO_CHAR(datee, ''DAY'', ''NLS_DATE_LANGUAGE=ENGLISH'') where "183400_Matches_number"='
|| v_number;
EXECUTE IMMEDIATE sql_qry INTO v_fecha;
IF v_fecha = 'SATURDAY'
THEN
UPDATE "183400_Matches_Details"
SET "datee" = "datee" - 1
WHERE "183400_Matches_number" = v_number;
ELSIF v_fecha = 'SUNDAY'
THEN
UPDATE "183400_Matches_Details"
SET "datee" = "datee" + 1
WHERE "183400_Matches_number" = v_number;
END IF;
RETURN to_date('19000101','yyyymmdd') ;
END;
/
After a second look I realized what your asking is actually quite simple: Given a date that is Sat update it to Fri, and that is Sun update to Mon. This can actually be done in a single SQL statement.
I changed it from a function to a procedure as the purpose is to Update the database, and return the number of rows processed. But as a function it makes the purpose to get the row count and updating the database as a side effect. Names and types (IMHO) should always reflect the purpose of the routine. I did 'return' the row count as an OUT parameter - it being an informational side effect. See fiddle for full example.
create or replace
procedure reschedule_sat_sun_match_details(
p_match_number in "183400_Matches_Details"."183400_Matches_number"%type
, p_rows_updated out number)
as
begin
update "183400_Matches_Details"
set "date" = case to_char("date", 'dy')
when 'sat' then "date"-1 -- Sat update to Fri
when 'sun' then "date"+1 -- Sun update to Mon
end
where to_char("date", 'dy') in ('sat','sun')
and "183400_Matches_number" = p_match_number;
p_rows_updated := sql%rowcount;
end reschedule_sat_sun_match_details;
For day of week values I used the format 'dy' rather than 'day'. The difference being 'dy' returns day name abbreviations with a constant length without padding, while 'day' pads the returned values to the length of the longest day name (to get constant length) thus "sunday" is returned as "sunday " to match the length of "wednesday".
A couple other suggestions. Avoid Mixed Case name and names beginning with numbers. These require double quoting (") on every reference. This becomes a pain to just write and your queries much harder to read and understand. a table name Matches_Details_183400 the exact same information without requiring the quotes. (Yes Oracle will make it upper case in messages it it issues but you can still write it in mixed case if you wish - it will still be the same name.) It gives you no benefit but a lot of pain.
As #hotfix mentioned do not use reserved or keywords as object names. Oracle has documented such words and reserves the right to enforce a specific meaning whenever they choose. If/When they do makes an almost untraceable bug to find.

Why Oracle PL/SQL insert into table is not working with cursor?

I'm trying to insert date into temporary table, but it's not saving any data. How should I change cursor date parameters?
The procedure is running without any error message but output xx_cdf_output_utl$.log('inside loop'); is not working.
xx_cdf_output_utl$.log('after loop'); log is printing fine.
Right now added three parameters: p_date_from (date), p_date_to (date), p_line_type_lookup_code (varchar2)
procedure gather_data(
-- p_valid_invoices_count out number,
-- p_invalid_invoices_count out number,
p_date_from in date,
p_date_to in date,
p_line_type_lookup_code in varchar2) is
ROUTINE constant varchar2(65) := PACKAGE_NAME||'.GATHER_DATA';
cursor c_inv(
p_date_from in date,
p_date_to in date,
p_line_type_lookup_code in varchar2) is
select
inv.invoice_id,
inv.invoice_num,
inv.invoice_amount,
inv.invoice_date,
inv.amount_paid,
pas.gross_amount,
pas.payment_num,
ven.vendor_id,
ven.vendor_name
from
ap_invoices_all inv, -- invoice table
ap_payment_schedules_all pas, -- payment schedules table
po_vendors ven -- vendors table
where
inv.invoice_date between p_date_from and p_date_to and
inv.wfapproval_status in (
'NOT REQUIRED',
'WFAPPROVED',
'MANUALLY APPROVED') and
pas.amount_remaining != 0 and
nvl(pas.hold_flag, 'N') != 'Y' and
(p_line_type_lookup_code is not null and
exists(
select
1
from
ap_invoice_distributions_all ind -- distribution table
where
ind.amount != 0 and
ind.line_type_lookup_code = p_line_type_lookup_code and
ind.invoice_id = inv.invoice_id) or
p_line_type_lookup_code is null) and
pas.invoice_id = inv.invoice_id and
ven.vendor_id = inv.vendor_id;
l_date_from date := nvl(to_date(p_date_from, 'yyyy-mm-dd'), to_date('2019-01-01', 'YYYY-MM-DD'));
l_date_to date := nvl(to_date(p_date_to, 'yyyy-mm-dd'), trunc(to_date(sysdate, 'YYYY-MM-DD')));
l_line_type_lookup_code varchar2(240) := p_line_type_lookup_code;
begin
for l_inv_rec in c_inv(
l_date_from,
l_date_to,
l_line_type_lookup_code)
loop
xx_cdf_output_utl$.log('inside loop');
insert into xx_zm_invoice_temp(
invoice_id,
invoice_num,
invoice_amount,
invoice_date,
amount_paid,
gross_amount,
payment_num,
vendor_id,
vendor_name)
-- vendor_amount_total,
-- vendor_amount_valid_flg)
values(
l_inv_rec.invoice_id,
l_inv_rec.invoice_num,
l_inv_rec.invoice_amount,
l_inv_rec.invoice_date,
l_inv_rec.amount_paid,
l_inv_rec.gross_amount,
l_inv_rec.payment_num,
l_inv_rec.vendor_id,
l_inv_rec.vendor_name);
end loop;
xx_cdf_output_utl$.log('after loop');
exception
when xx_cdf_error_utl$.e_internal_exception then
xx_cdf_error_utl$.raise_error;
when others then
xx_cdf_error_utl$.output_unexp_exception(
p_routine => ROUTINE);
end;
When you do:
l_date_from date := nvl(to_date(p_date_from, 'yyyy-mm-dd'), to_date('2019-01-01', 'YYYY-MM-DD'));
l_date_to date := nvl(to_date(p_date_to, 'yyyy-mm-dd'), trunc(to_date(sysdate, 'YYYY-MM-DD')));
the
to_date(p_date_to, 'yyyy-mm-dd')
has to convert p_date from a date to a string first, and it will use the session NLS parameters to do that. As it isn't erroring I imagine your date format has a 2-digit year, possibly the kind-of-default DD-MON-RR. You are effectively doing something like:
to_date(to_char(p_date_to, 'dd-mon-rr'), 'yyyy-mm-dd')
which would translate today's date to 0023-03-20, which isn't what you intended at all. You end up looking for a range of dates in year 0023, or maybe 0020, so it's not surprising you do't find any matching data.
You don't need to convert the parameters to or from strings, you can do:
l_date_from date := nvl(p_date_from, date '2019-01-01');
l_date_to date := nvl(p_date_to, trunc(sysdate));
You can truncate the parameter values if you think that might be necessary, but with a trunc() call, not by bouncing through strings.
You don't really need l_date_from or l_date_to though (or l_line_type_lookup_code); you could do the nvl() firstly in the cursor call:
for l_inv_rec in c_inv(
nvl(p_date_from, date '2019-01-01'),
nvl(p_date_to, trunc(sysdate)),
p_line_type_lookup_code)
Having the same name for the procedure parameters and the cursor parameters might be confusing - though the way you're currently using them, the cursor doesn't really need the parameters - it can refer directly to the procedure values, and use nvl() internally:
inv.invoice_date between nvl(p_date_from, date '2019-01-01') and nvl(p_date_to, trunc(sysdate)) and
I'm always a bit wary of using between with dates because it's easy to forget it's inclusive, and you can accidentally pick up the same data - at exactly midnight - in more than one call. It's a bit safer and clearer to use a range:
inv.invoice_date >= nvl(p_date_from, date '2019-01-01') and
inv.invoice_date < nvl(p_date_to, trunc(sysdate)) and
decided explicitly if the last condition should use < or <=. The latter is usually what you mean if the table values have non-midnight times.
And you could use an implicit cursor, but that's partly a matter of taste...
You could even give your procedure arguments default values, but you might be restricted in how it's called.

Update query failed '0' rows affected

I am trying to run this query but the query keeps giving up on me:
Update StockInvoiceInfo set Quantity = Quantity - 2 where p_id = 5 AND ProductDate = convert(Cast('31-5-2015' as datetime)) ;
After Running this code it returns an error below:
Incorrect syntax near '31-5-2015'
The datatype of the ProductDate column isDate. I am using Sql Server 2012.
You have used Convert functions but didn't supplied it with parameters. Also there is no need for this function here. Also take care of date format. I have changed it to standard format:
Update StockInvoiceInfo set Quantity = Quantity - 2
where p_id = 5 AND ProductDate = Cast('2015-05-31' as datetime)
If all you are trying to do is compare a Sql Date, then just use an agnostic format like '20150531' or easier to read '2015-05-31'. No need for casts or convert at all, i.e.
WHERE ... AND ProductDate = '2015-05-31'
However, if ProductDate isn't a date, but one of the *DATETIME* data types, and you are looking to update any time on the same day, then I believe you are looking for something like:
Update StockInvoiceInfo
set Quantity = Quantity - 2
where
p_id = 5
AND CAST(ProductDate AS DATE) = '2015-05-31';
However, the performance will be lousy as the clause isn't likely to be SARGable. You're better off simply doing:
AND ProductDate >= '2015-05-31' AND ProductDate < '2015-06-01';
(Resist the temptation to use between, or hacks like ':23:59:59' as there will be data corner cases which will bite you)
use CAST('5-31-2015' as DATETIME)
with the above update statement you started convert but with incomplete syntax
here the convert syntax
Update StockInvoiceInfo
set Quantity = Quantity - 2
where p_id = 5
AND ProductDate = convert(datetime,Cast('31-5-2015' as varchar),103) ;

convert string date to date type in sql server 2012

In a school website, I want to enable the admin to filter students based on date range when they were born. Dates in my tblStudent are stored as strings, so I cannot use:
SELECT ts.Name from tblStudent ts WHERE ts.BirthDay>'1367/01/31' AND ts.BirthDay<'1377/01/31'
I have saved dates (Jalali Format) in database table tblStudent. I need to do comparison based on dates. So I need to convert date strings to date type in sql server. To this purpose I used:
SELECT convert(date,tblStudent.BirthDay) from tblStudent
However,It stops after 27 results with the following error
Msg 241, Level 16, State 1, Line 1
Conversion failed when converting date and/or time from character string.
I have the following date strings in my tblStudent table.
1379/09/01
1375/04/20
1378/03/02
1378/03/21
1378/04/18
1378/04/18
1378/05/05
1375/04/20
1379/01/03
1378/03/01
1370/09/09
1378/03/22
1375/09/15
1379/09/01
1379/09/10
1375/04/08
1375/05/06
1370/09/09
1379/10/10
1375/04/10
1375/11/01
1375/04/04
1375/08/11
1375/05/05
1376/09/19
1375/12/12
1376/01/13
1375/15/10
1375/04/14
1375/04/04
1375/05/14
1374/11/11
1375/05/30
1375/05/14
1377/12/13
1377/02/31
1377/12/14
1377/01/13
1375/05/31
1377/11/05
1377/07/05
1375/05/31
1377/03/01
1377/04/01
1377/05/02
1377/05/04
1377/03/03
1377/01/14
1377/05/30
1377/04/31
1375/05/30
1376/06/12
1375/12/10
1377/08/14
1377/03/04
1375/04/08
1375/07/18
1375/08/09
1375/09/12
1375/11/12
1376/12/12
1375/01/02
1375/05/09
1375/04/09
1376/01/01
1375/01/30
1377/04/04
1375/05/23
1375/05/01
1377/02/01
1367/12/05
1375/05/31
1373/03/29
1373/03/03
1375/05/05
Is there a way to convert these string dates to date type and then compare them with some query? For example, such a query can be:
SELECT ts.Name from tblStudent ts where ts.BirthDay>'1375/05/31'
I think you can make them ints and compare them:
SELECT ts.Name
FROM tblStudent ts
WHERE CONVERT(INT,REPLACE(ts.BirthDay,'/','') > 13670131
AND CONVERT(INT,REPLACE(ts.BirthDay,'/','') < 13770131
Or for your second example:
SELECT ts.Name
FROM tblStudent ts
WHERE CONVERT(INT,REPLACE(ts.BirthDay,'/','') > 13750531
This would work because having the order Year-Month-Day will ensure that the int representation of a later time will be greater than the int representation of an earlier time.
I really do not know if this is the best idea, but it is an idea of how to do it. After all you would be using a conversion.
From C# you have a few options:
If your input is string:
var dateInt = Int32.Parse(dateString.Replace("/",""));
If your input is Date then:
var dateInt = Int32.Parse(dateValue.ToString("yyyyMMdd"));
You could also pass the string itself in the db and let the db do the work for you :
DECLARE #Date AS VARCHAR(10)
SET #Date = ...--This will be filled with the inputed string
DECLARE #DateINT AS INT
SET #DateINT = CONVERT(INT,REPLACE(#Date,"/",""))

Resources