Check if anniversary month is within a range of months - mysql

I'm trying to execute some different logic in a stored procedure when processing the anniversary month. Using a simple IF(anniversary = MONTH(x)) works fine when only one month is processed.
However, when multiple months are processed, the differing logic needs to be executed if the anniversary month is between the start month and the end month. IF(anniversary BETWEEN MONTH(x) AND MONTH(y)) will not work over the new year (i.e. September–January).
The anniversary field is currently generated as MONTH(contract_start), but this can be changed (it was previously using MONTHNAME() before, I have no idea why).
I was thinking that it might be a good idea for anniversary to become a boolean, to make the IF function more readable.

Okay, I worked this out. It was simple once I wrote down the algoritm in pseudocode
I've created a function to store the logic instead, but it's fairly easy to turn it into an expression if needed.
BEGIN
IF min != max
THEN
IF YEAR(min) != YEAR(max)
THEN
IF MONTH(born) BETWEEN MONTH(min) AND 12
OR MONTH(born) BETWEEN 1 AND MONTH(max)
THEN RETURN 1;
ELSE RETURN 0;
END IF;
ELSEIF MONTH(born) BETWEEN MONTH(min) AND MONTH(max)
THEN RETURN 1;
ELSE RETURN 0;
END IF;
ELSEIF MONTH(born) = MONTH(min)
THEN RETURN 1;
ELSE RETURN 0;
END IF;
END

Related

Legend changing when report is deployed or exported

I have a 100% Stacked Bar chart. Students are banded based on their attendance,and the report simply tracks changes in the band populations as a percentage of the total student population. In Report Builder, this works totally fine (except in that it highlights our rubbish attendance of course...)
The problem arises when:
The report is exported from Report Builder to PDF/Word/Excel/whatever
The report is deployed to an SSRS server and run through the browser
You change to a subsequent page of the report, and then change back to the page with the graph.
In all case although the actual chart remains unchanged, the Legend loses its mind a little bit and shows the top three items as 100%:
I can't think of any reason that that should happen...the report was particularly finicky to make as a result of the underlying data structure (which regrettably is based on a Report Model, meaning I can't tweak it with SQL) and I had to use custom vb code in the end to get it to do what I wanted, but I can't see why any of that should change its behaviour either on the server or when exported.
So my question is; why does this happen, and how do I stop it happening?
EDIT: By Request:
The dataset inherently returns data in the format below. There's a row per learner ID per "Week Start Date".
The custom code I am using is pasted below (inept I know - no laughing!):
Private attendance_table As New System.Collections.Hashtable()
Private last_added_table As New System.Collections.Hashtable()
Public Function band_calc(ByVal attendance As Double) As String
REM Define the bands that I want to track
If attendance = 1 Then
Return "A"
ElseIf attendance >= 0.975 Then
Return "B"
ElseIf attendance >= 0.95 Then
Return "C"
ElseIf attendance >= 0.925 Then
Return "D"
ElseIf attendance >= 0.90 Then
Return "E"
ElseIf attendance >= 0.85 Then
Return "F"
ElseIf attendance >= 0.8 Then
Return "G"
Else
Return "X"
End If
End Function
Public Function get_attendance_band(ByVal week_start_date as String, ByVal learnerID As Integer, ByVal possibles As Integer, ByVal presents As Integer) As String
If attendance_table Is Nothing Then
Dim attendance_table As New System.Collections.Hashtable()
End If
If last_added_table Is Nothing Then
Dim last_added_table As New System.Collections.Hashtable()
End If
REM check if attendance_table has the Learner already
If attendance_table.ContainsKey(learnerID) Then
REM check if we've already added this week's data in
If attendance_table(learnerID).ContainsKey(week_start_date) Then
REM just return the band_calc for those data
Return band_calc(attendance_table(learnerID)(week_start_date)(1) / attendance_table(learnerID)(week_start_date)(0))
Else
REM Add in this week to the hashtable. Add this weeks data to the last weeks data
attendance_table(learnerID).Add(week_start_date, New Object() { possibles + attendance_table(learnerID)(last_added_table(learnerID))(0), presents + attendance_table(learnerID)(last_added_table(learnerID))(1)})
REM record that this is now the last date updated for this learner
last_added_table(learnerID) = week_start_date
REM show the band!
Return band_calc(attendance_table(learnerID)(week_start_date)(1) / attendance_table(learnerID)(week_start_date)(0))
End If
Else
attendance_table.Add(learnerID, New System.Collections.Hashtable())
attendance_table(learnerID).Add(week_start_date, New Object() {possibles, presents})
last_added_table.Add(learnerID, week_start_date)
Return band_calc(attendance_table(learnerID)(week_start_date)(1) / attendance_table(learnerID)(week_start_date)(0))
End If
End Function
For the series properties; The sort, group and label (which defines the Legend obviously) are all set to this:

How to improve performance for REGEXP string matching in MySQL?

Preface:
I've done quite a bit of (re)searching on this, and found the following SO post/answer: https://stackoverflow.com/a/5361490/6095216 which was pretty close to what I'm looking for. The same code, but with somewhat more helpful comments, appears here: http://thenoyes.com/littlenoise/?p=136 .
Problem Description:
I need to split 1 column of MySQL TEXT data into multiple columns, where the original data has this format (N <= 7):
{"field1":"value1","field2":"value2",...,"fieldN":"valueN"}
As you might guess, I only need to extract the values, putting each one into a separate (predefined) column. The problem is that the number and order of the fields is not guaranteed to be the same for all records. Thus, solutions using SUBSTR/LOCATE, etc. don't work, and I need to use regular expressions. Another restriction is that 3rd party libraries such as LIB_MYSQLUDF_PREG (suggested in the answer from my 1st link above) cannot be used.
Solution/Progress so far:
I've modified the code from the above links such that it returns the first/shortest match, left-to-right; otherwise, NULL is returned. I also refactored it a bit and made the identifiers more reader/maintainer-friendly :)
Here's my version:
CREATE FUNCTION REGEXP_EXTRACT_SHORTEST(string TEXT, exp TEXT)
RETURNS TEXT DETERMINISTIC
BEGIN
DECLARE adjustStart, adjustEnd BOOLEAN DEFAULT TRUE;
DECLARE startInd INT DEFAULT 1;
DECLARE endInd, strLen INT;
DECLARE candidate TEXT;
IF string NOT REGEXP exp THEN
RETURN NULL;
END IF;
IF LEFT(exp, 1) = '^' THEN
SET adjustStart = FALSE;
ELSE
SET exp = CONCAT('^', exp);
END IF;
IF RIGHT(exp, 1) = '$' THEN
SET adjustEnd = FALSE;
ELSE
SET exp = CONCAT(exp, '$');
END IF;
SET strLen = LENGTH(string);
StartIndLoop: WHILE (startInd <= strLen) DO
IF adjustEnd THEN
SET endInd = startInd;
ELSE
SET endInd = strLen;
END IF;
EndIndLoop: WHILE (endInd <= strLen) DO
SET candidate = SUBSTRING(string FROM startInd FOR (endInd - startInd + 1));
IF candidate REGEXP exp THEN
RETURN candidate;
END IF;
IF adjustEnd THEN
SET endInd = endInd + 1;
ELSE
LEAVE EndIndLoop;
END IF;
END WHILE EndIndLoop;
IF adjustStart THEN
SET startInd = startInd + 1;
ELSE
LEAVE StartIndLoop;
END IF;
END WHILE StartIndLoop;
RETURN NULL;
END;
I then added a helper function to avoid having to repeat the regex pattern, which, as you can see from above, is the same for all the fields. Here is that function (I left my attempt to use a lookbehind - unsupported in MySQL - as a comment):
CREATE FUNCTION GET_MY_FLD_VAL(inputStr TEXT, fldName TEXT)
RETURNS TEXT DETERMINISTIC
BEGIN
DECLARE valPattern TEXT DEFAULT '"[^"]+"'; /* MySQL doesn't support lookaround :( '(?<=^.{1})"[^"]+"'*/
DECLARE fldNamePat TEXT DEFAULT CONCAT('"', fldName, '":');
DECLARE discardLen INT UNSIGNED DEFAULT LENGTH(fldNamePat) + 2;
DECLARE matchResult TEXT DEFAULT REGEXP_EXTRACT_SHORTEST(inputStr, CONCAT(fldNamePat, valPattern));
RETURN SUBSTRING(matchResult FROM discardLen FOR LENGTH(matchResult) - discardLen);
END;
Currently, all I'm trying to do is a simple SELECT query using the above code. It works correctly, BUT IT. IS. SLOOOOOOOW... There are only 7 fields/columns to split into, max (not all records have all 7)! Limited to 20 records, it takes about 3 minutes - and I have about 40,000 records total (not very much for a database, right?!) :)
And so, finally, we get to the actual question: [how] can the above algorithm/code (pretty much a brute search at this point) be improved SIGNIFICANTLY performance-wise, such that it can be run on the actual database in a reasonable amount of time? I started looking into the major known pattern-matching algorithms, but quickly got lost trying to figure out what would be appropriate here, in large part due to the number of available options and their respective restrictions, conditions for use, etc. Plus, it seems like implementing one of these in SQL just to see if it would help, might be a lot of work.
Note: this is my first post ever(!), so please let me know (nicely) if something is not clear, etc. and I will do my best to fix it. Thanks in advance.
I was able to solve this by parsing the JSON, as suggested by tadman and Matt Raines above. Being new to the concept of JSON, I just didn't realize it could be done this way at all...a little embarrassing, but lesson learned!
Anyway, I used the get_option function in the common_schema framework: https://code.google.com/archive/p/common-schema/ (found through this post, which also demonstrates how to use the function: Parse JSON in MySQL ). As a result, my INSERT query took about 15 minutes to run, vs the 30+ hours it would've taken with the REGEXP solution. Thanks, and until next time! :)
Don't do it in SQL; do it in PHP or some other language that has builtin tools for parsing JSON.

FFT implementation in Verilog : Error using nested for loop

This post is related to the my previous post related to FFT.
FFT implemetation in Verilog: Assigning Wire input to Register type array
I want to assign output of first stage to input of second stage of FFT butterfly modules. I have to re-order the output of first stage according to input of second stage. Here is my code to implement the swapping.
always# (posedge y_ndd[0] or posedge J)
begin
if(J==1'b1)
begin
for (idx=0; idx<N/2; idx=idx+1)
begin
IN[2*idx] <= X[idx*2*X_WDTH+: 2*X_WDTH];
IN[2*idx+1] <= X[(idx+N/2)*2*X_WDTH+: 2*X_WDTH];
end
end
else
begin
level=level+1;
modulecount=0;
for(jj=0;jj<N;jj=jj+(2**(level+1)))
begin
for (jx=jj; jx<jj+(2**level); jx=jx+1)//jj+(2**level)
begin
IN[modulecount] <=OUT[jx];
IN[modulecount+1] <=OUT[jx+(2**level)];
modulecount=modulecount+1;
end
end
end
end
When I synthesize this, It gives 2 errors.
ERROR:Xst:891 - "Network.v" line 161: For Statement is only supported when the new step evaluation is constant increment or decrement of the loop variable.
ERROR:Xst:2634 - "Network.v" line 161: For loop stop condition should depend on loop variable or be static.
Can't we use non-constant increment and non-static stop coditions?
If that so, how we handle this.
Any help is appreciated.
Thanks in advance.
Synthesis tools unroll loops in order to synthesize the circuit. Therefore, only loops that iterate a constant number of times, whose constant is known at compile/elaboration time are synthetisable.
When the stop value is not known, you can assume a maximum number of iterations and use that as the stop condition. Then add the original stop condition as a conditional statement inside the loop:
for (jx=jj; jx < MAX_LOOP_ITERATION; jx=jx+1)//jj+(2**level)
begin
if (jx<jj+(2**level)) // <---------- Add stop condition here
begin
IN[modulecount] <=OUT[jx];
IN[modulecount+1] <=OUT[jx+(2**level)];
modulecount=modulecount+1;
end
end
If N is not a constant, the outer loop should also be fixed using a similar conditional statement. You also need to fix the increment value and each time add a constant value. Use a conditional statement to check if jj==jj+(2**(level+1))
Obviously, you need to be careful as a high max number may increase your worst case delay and the minimum clock cycle time.
//ll,level,K has to be declare.
always# (posedge y_ndd[0] or posedge J)
begin
if(J==1'b1)
begin
for (idx=0; idx<N/2; idx=idx+1)
begin
IN[2*idx] <= X[idx*2*X_WDTH+: 2*X_WDTH];
IN[2*idx+1] <= X[(idx+N/2)*2*X_WDTH+: 2*X_WDTH];
end
end
else
begin
ll=ll+1;
modulecount=0;
for(level=0;level<K;level=level+1) //K time you need to execute
begin
if(ll==level)
begin
for(jj=0;jj<N;jj=jj+(2**(level+1)))
begin
for (jx=jj; jx<jj+(2**level); jx=jx+1)
begin
IN[modulecount] <=OUT[jx];
IN[modulecount+1] <=OUT[jx+(2**level)];
modulecount=modulecount+1;
end
end
end
end
//ll=ll+1;
end
end
You can try this. It has to work. But problem is outer loop will execute K times.

How to use goto label in MySQL stored function

I would like to use goto in MySQL stored function.
How can I use?
Sample code is:
if (action = 'D') then
if (rowcount > 0) then
DELETE FROM datatable WHERE id = 2;
else
SET p=CONCAT('Can not delete',#b);
goto ret_label;
end if;
end if;
Label: ret_label;
return 0;
There are GOTO cases which can't be implemented in MySQL, like jumping backwards in code (and a good thing, too).
But for something like your example where you want to jump out of everything to a final series of statements, you can create a BEGIN / END block surrounding the code to jump out of:
aBlock:BEGIN
if (action = 'D') then
if (rowcount > 0) then
DELETE FROM datatable WHERE id = 2;
else
SET p=CONCAT('Can not delete',#b);
LEAVE aBlock;
end if;
end if;
END aBlock;
return 0;
Since your code is just some nested IFs, the construct is unnecessary in the given code. But it makes more sense for LOOP/WHILE/REPEAT to avoid multiple RETURN statements from inside a loop and to consolidate final processing (a little like TRY / FINALLY).
There is no GOTO in MySQL Stored Procs. You can refer to this post:
MySQL :: Re: Goto Statement

Inherited database has leap year code that compiler doesn't like

In my job, I have inherited an Access 97 database. This database is very unstable and I need to remedy that in one way or another. I have been trying to go through and debug the current version so that I can migrate it to 2007. I have run across some code that the compiler doesn't like and not sure how to fix it...here is the code:
Function DaysInMonth(ByVal D As Date) As Long
' Requires a date argument because February can change
' if it's a leap year.
Select Case Month(D)
Case 2
If LeapYear(Year(D)) Then
DaysInMonth = 29
Else
DaysInMonth = 28
End If
Case 4, 6, 9, 11
DaysInMonth = 30
Case 1, 3, 5, 7, 8, 10, 12
DaysInMonth = 31
End Select
End Function
I get a compile error: Sub or Function not defined and it highlights the first "LeapYear".
Any help at all would be greatly appreciated! Thanks!
LeapYear is another function or procedure that appears not be present in your modules or has been made Private. LeapYear isn't a VBA function. There must have been a function that takes a year Year(D) and returns TRUE or FALSE if it's a leapyear. either insert one or set the existing one to Public
Edit:You could use IsLeapYear but change to 'LeapYear' and call using IsLeapYear(D)
The code in question is idiotic -- it was clearly written by somebody who didn't have a clue about VBA dates, which already know everything that is needed without needed to encode this crap into a CASE SELECT.
This expression will get you the number of days in a month:
Day(DateAdd("m", 1, DateValue(Month(Date()) & "/1/" & Year(Date()))) - 1)
What this does is get the first of the current month, adds a month to it (for the first of the next month), and then subtracts 1 from it. Since the integer part of the VBA date type is the day part, that will get you the last day of the current month. Then you take the result and pull the day out with the Day() function.
Coding that up as a function:
Function DaysInMonth(ByVal dteDate As Date) As Integer
Dim dteFirstOfMonth As Date
Dim dteLastOfMonth As Date
dteFirstOfMonth = DateValue(Month(dteDate) & "/1/" & Year(dteDate))
dteLastOfMonth = DateAdd("m", 1, dteFirstOfMonth) - 1
DaysInMonth = Day(dteLastOfMonth)
End Function
You could also code this up using the fact that the DateSerial() function treats the zeroth day as the last of the previous month:
Function DaysInMonth(ByVal dteDate As Date) As Integer
Dim dteOneMonthFromDate As Date
Dim dteLastOfThisMonth As Date
dteOneMonthFromDate = DateAdd("m", 1, dteDate)
dteLastOfThisMonth = DateSerial(Year(dteOneMonthFromDate), Month(dteOneMonthFromDate), 0)
DaysInMonth = Day(dteLastOfThisMonth)
End Function
But that doesn't make it any shorter...
None of this requires figuring out leap year rules -- those are built into the VBA date type.
And, of course, the function should not return a Long, but an Integer, since the maximum value it can ever return is 31.
LeapYear may not be your only issue.
In Access '97, go to the VBA editor and click "Tools/References":
Look in the references of your '97 project and see what DLLs are listed.
A screen will appear that shows you the ActiveX DLLs that can be used for the project. The ones that are checked are the ones currently used:
Odds are there is a DLL there that needs to be referenced in your new 2007 database.