most efficient method for combining query results - linq

This question is somewhat exploratory as I decided to ask for advice/suggestions prior to attempting implementation in the hopes of a more timely path to the result. I have a database that stores intraday data for commodities and equities in 1 minute formats. The rationale behind this was that if I had 1 minute bars, I could create any time series bar that I wanted(i.e., 5 min , 15 min, 60 min etc,etc)
I thought about going the route where, for example, on a 15 minute chart I would simply iterate through the whole subset of results for a given symbol, look for rows with timestamps of :01,:16,:31:46 to begin capturing the open, close, maxhigh and minlow and cumulative volume and create new datapoint from that amalgamation. The problem with that is that, for commodities especially that trade 24 hours, there is not always a bar for every minute so there COULD be a situation where there isn't a :16 bar, or a :31 bar for a given symbol. This could foul the whole sequence of getting the bars data correctly. This also eliminated the possibility of just grabbing 15 bars at a time and capturing the open, close, maxhigh and minlow and cumulative volume. (Naturally, the table contains symbol, datetime, open,high,low,close,volume)
In order for either of the above to work I would have to "fix" the database every night by checking for missing bars and copying the prior bar to create a bar for that missing row. This is not the preferred method but Ill make it happen if necessary.
I am looking for any guidance on a proper path, whether from past experience or from reading about this problem here.
UPDATE:
Here is the code I've come up with that works BUT it is a little slow, it takes approximately 30 seconds to create 2 months of 30 minute bars from the 1 minute data in the database table.
Partial Class testintracharts
Inherits Page
<WebMethod>
Public Shared Function GetBars(ByVal symbol As String, ByVal seriesInterval As Integer) As List(Of ArrayList)
Dim barsList As New List(Of ArrayList)
'replace with TD model
Using ctx As New BATLEntities()
ctx.Configuration.AutoDetectChangesEnabled = False
'get all days for a given symbol, ordered from oldest to newest
Dim dateList As List(Of DateTime) = GetDistinctDates(ctx, symbol, seriesInterval).ToList()
'cache data series toa list for the given symbol
Dim seriesList As List(Of tsintrachart) = GetSymbolSeries(ctx, symbol)
If Not dateList Is Nothing And dateList.Any() Then
For Each seriesDate As DateTime In dateList
Dim curTime As TimeSpan = New TimeSpan(0, 1, 0)
Dim maxTime As TimeSpan = New TimeSpan(24, 0, 0)
'loop through the data series for the given day
'series start at 00:01:00
'series ends at 00:00:00 (next day)
While curTime < maxTime
Dim seriesMax As TimeSpan = curTime.Add(New TimeSpan(0, seriesInterval - 1, 0))
'get the data chunk based on series interval
'special condition when seriesmax reaches 24:00:00, in TimeSpan this becomes 1.00:00:00 (SQL doesn't like this)
'query needs to incorporate 00:00:00 of next day as the last entry for this time series
Dim data As List(Of tsintrachart) = Nothing
If TimeSpan.Compare(seriesMax, maxTime) = 0 Then
Dim nextSeriesDate As DateTime = seriesDate.AddDays(1)
data = GetDataSeries(seriesList, seriesDate, curTime, nextSeriesDate, New TimeSpan(0, 0, 0))
Else
data = GetDataSeries(seriesList, seriesDate, curTime, seriesDate, seriesMax)
End If
If Not data Is Nothing And data.Any() Then
Dim lastbarnum As Integer = data.Count - 1
Dim intradayDatum As New ArrayList()
With intradayDatum
.Add(DateTimeToUnixTimestamp(seriesDate.Add(seriesMax))) 'date
.Add(data(0).Open)
'.Add((From d In data Where TimeSpan.Compare(d.Time, curTime) = 0 Select d.Open).FirstOrDefault()) 'open
.Add((From d In data Select d.High).Max()) 'high
.Add((From d In data Select d.Low).Min()) 'low
.Add(data(lastbarnum).Close)
'.Add((From d In data Where TimeSpan.Compare(d.Time, seriesMax) = 0 Select d.Close).FirstOrDefault()) 'close or last sale
.Add((From d In data Select d.Volume).Sum()) 'volume
End With
barsList.Add(intradayDatum)
End If
'update current series start time, move to next series chunk
curTime = curTime.Add(New TimeSpan(0, seriesInterval, 0))
End While
Next
End If
End Using
Return barsList
End Function
Private Shared Function GetDataSeries(ByRef list As List(Of tsintrachart), ByVal startDate As DateTime, ByVal startSpan As TimeSpan, ByVal endDate As DateTime, ByVal endSpan As TimeSpan) As List(Of tsintrachart)
'LINQ, where symbol/time>=startRange/time<=endRange
Dim series = From data In list
Where (TimeSpan.Compare(data.Time, startSpan) >= 0 AndAlso data.Date = startDate) _
And (TimeSpan.Compare(data.Time, endSpan) <= 0 AndAlso data.Date = endDate)
Select data
Return series.ToList()
End Function
Private Shared Function GetSymbolSeries(ByRef ctx As BATLEntities, ByVal symbol As String) As List(Of tsintrachart)
Dim series = From data In ctx.tsintracharts
Where (data.Symbol = symbol)
Select data
Return series.ToList()
End Function
Private Shared Function GetDistinctDates(ByRef ctx As BATLEntities, ByVal symbol As String, ByVal interval As Integer) As IQueryable(Of DateTime)
Dim numDates As Integer = TakeValue(interval)
Dim dates = (From data In ctx.tsintracharts
Where data.Symbol = symbol
Select data.Date
Order By [Date]).Distinct().Take(numDates)
Return dates
End Function
Private Shared Function TakeValue(ByVal interval As Integer) As Integer
Select Case interval
Case 0 To 15
Return 1440
Case 16 To 30
Return 720
Case 31 To 60
Return 528
Case Else
Return 400
End Select
End Function
End Class

You could create a table that holds all possible 1min points in time. Then you can OUTER JOIN to that table to make the server fill in any gaps. The table would be like this:
CREATE TABLE TimePoints (
DateTime DATETIME2(0) PRIMARY KEY
)
And you have to fill it will a few decades of data. To query, you would join to it:
SELECT *
FROM TimePoints
LEFT JOIN myOtherTable ON ...
WHERE TimePoints.DateTime >= (nowMinus15min) AND TimePoints.DateTime <= now

Related

FILTER OLAP CUBE WITH VBA

I am trying to update an OLAP pivotfilter with the value of another cell (not in a pivot table).
The code I have is as follows:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'This line stops the worksheet updating on every change, it only updates when
'cell C1 or C2 is touched
If Intersect(Target, Range("C1:C2")) Is Nothing Then Exit Sub
'Set the Variables to be used
Dim pt As PivotTable
Dim Field As PivotField
Dim NewStock As String
'Here you amend to suit your data
Set pt = ActiveSheet.PivotTables("PivotFilter")
'The activesheet is named "Top1_Value"
Set Field = pt.PivotFields("[StockCode].[Stock Code_SKU].[Stock Code_SKU]")
NewStock = ActiveSheet.Range("C1").Value
'This updates and refreshes the PIVOT table
With pt
Field.ClearAllFilters
Field.VisibleItemsList = NewStock
pt.RefreshTable
End With
End Sub
Any assistance to resolve would be appreciated.

Define a date offset from today for a batch report

I have to make a report that will execute automatically several times, every day, with different settings. The report selects records between two dates. Now, the client wants to be free to define, in a text or excel file, the dates that will run each report. For example, every day I want to run the report for that day, and for that same day the previous year, and for the same day the previous week, and for the previous week, and for the next month of the previous year. Then on the first of every month, the whole previous month, etc. I think you get the gist.
My question is: Is there any established way of doing this? Some text encoding for offsets of dates? I've looked and nothing appears. However, it doesn't seem an outlandish proposition. I suppose the situation has happened before. I wouldn't like to reinvent the wheel. If, however, there is nothing, any idea would be welcome :-)
OK, so I did it myself, no free lunch this time :-) The format I chose is a string like:
+1W,fW,-1D (That would be the next week, first day of that week, then one day before that)
Separator is comma, keys are D for day, W for week, M for month, Y for year, F for first, L for Last (all keys can be lower or upper case). The format ignores whitespace, but the last non-whitespace character must be a key. Only integer offsets allowed.
I give my first implementation of a VBA function that interprets a text offset. It seems to work, although little testing done yet.
Well, that's all, for posterity.
Public Function GetDateFromOffset(fecIni As Date, sDif As String) As Date
Dim sArr() As String, ii As Integer, fRes As Date
fRes = fecIni
sArr = Split(sDif, ",")
For ii = LBound(sArr()) To UBound(sArr())
fRes = ApplyOneOffset(fRes, sArr(ii))
Next ii
GetDateFromOffset = fRes
End Function
Public Function ApplyOneOffset(fecIni As Date, sDif As String) As Date
Const C_DAY As String = "D", C_WEEK As String = "W", C_MONTH As String = "M", C_YEAR As String = "Y"
Const C_FIRST As String = "F", C_LAST As String = "L"
Dim iDesp As Integer, sDesp As String, fRes As Date
sDesp = UCase(Right(sDif, 1))
sDif = Trim(UCase(Left(sDif, Len(sDif) - 1)))
Select Case sDesp
Case C_DAY
If (IsNumeric(sDif)) Then
fRes = DateAdd("d", CInt(sDif), fecIni)
End If
Case C_WEEK
If (sDif = C_FIRST) Then
fRes = dhFirstDayInWeek(fecIni)
ElseIf (sDif = C_LAST) Then
fRes = dhLastDayInWeek(fecIni)
ElseIf (IsNumeric(sDif)) Then
fRes = DateAdd("ww", CInt(sDif), fecIni)
End If
Case C_MONTH
If (sDif = C_FIRST) Then
fRes = dhFirstDayInMonth(fecIni)
ElseIf (sDif = C_LAST) Then
fRes = dhLastDayInMonth(fecIni)
ElseIf (IsNumeric(sDif)) Then
fRes = DateAdd("m", CInt(sDif), fecIni)
End If
Case C_YEAR
If (sDif = C_FIRST) Then
fRes = DateSerial(YEAR(fecIni), 1, 1)
ElseIf (sDif = C_LAST) Then
fRes = DateSerial(YEAR(fecIni), 31, 12)
ElseIf (IsNumeric(sDif)) Then
fRes = DateAdd("yyyy", CInt(sDif), fecIni)
End If
Case Else
fRes = fecIni
End Select
ApplyOneOffset = fRes
End Function

Excel VBA - Stops Selecting Visible Rows on Filter after many interations

My code takes a file name from TextBox1, opens that file and indexes through all the unique values in column B. It then takes a second file, file name in TextBox2, and filters it based on the current index from the first file. It then takes the filtered results and copies them to a sheet in the new workbook. A new sheet is then generated in the new workbook to paste the next filtered result.
My issue is that I have many rows of data and for some reason the filtered data is not be selected after many iterations. My program selects all filtered data when it starts, but at some point it just begins selecting the headers instead of all the visible cells. Let me know if I am missing something or if there is a quick workaround. Thank you.
Sub NewFileGenerate()
Dim I As Integer
Dim N As Integer
Dim X As Integer
Dim IndexedCell As String
X = 1
Windows(TextBox1.Value).Activate
'Need to count only populated cells
I = Sheets(1).Columns(2).Cells.SpecialCells(xlCellTypeConstants).Count
Set Newbook = Workbooks.Add
For N = 2 To I - 1
Application.CutCopyMode = True
Windows(TextBox1.Value).Activate
IndexedCell = Cells(N, 2).Value
Windows(TextBox2.Value).Activate
With Sheets(1)
With .Range("A1", "Z" & I - 1)
.AutoFilter Field:=5, Criteria1:=IndexedContract
.SpecialCells(xlCellTypeVisible).Copy
End With
End With
Newbook.Activate
ActiveSheet.Paste Destination:=Sheets(X).Range("A1:Z1")
Cells.Select
Selection.Font.Size = 10
Cells.EntireColumn.AutoFit
Cells.Select
X = X + 1
If X > 3 Then
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = "Sheet" & X
End If
Application.CutCopyMode = False
Next N
End Sub
I'm guessing that your source worksheet (textbox2.value) has more rows than your index worksheet (textbox1.value). You set I equal to the number of rows in your index worksheet, then you tell the autofilter to only use that number of rows. You need to change the line "With .Range("A1", "Z" & I - 1)" so it picks up all of the rows in your source worksheet.

strange behaviour of linq usage

I have two tables (t1 and t2) and I select two fields from this tables (f1 and f2).
The list Queries contains the selected data. In this case there are 2 entry with 2 rows.
This is the code:
Dim FieldIndexes As New List(Of Integer)
Dim Queries As New List(Of IEnumerable(Of Object()))
For i = 0 To _SqlSyntaxChecker.QueriedTables.Count - 1
FieldIndexes.Clear()
For j = 0 To _SqlSyntaxChecker.DataFields.Count - 1
If _SqlSyntaxChecker.QueriedTables(i).TableName = _SqlSyntaxChecker.DataFields(j).TableName Then FieldIndexes.Add(_SqlSyntaxChecker.DataFields(j).FieldIndexInDataTable)
Next
Dim query = _SqlSyntaxChecker.QueriedTables(i).Rows.Select(Function(Row) FieldIndexes.Select(Function(FieldIndex) Row.Item(FieldIndex)).ToArray)
Queries.Add(query)
For Each item In Queries(i)
_OutputDataTable.Rows.Add(item)
Next
Next
And this is the result:
As you see, everything is ok, I expected this result (it is now not important, that I have on the image 4 rows in one column).
Originally, I wanted to populate the _OutputDataTable outside the for cykle, like so:
Dim FieldIndexes As New List(Of Integer)
Dim Queries As New List(Of IEnumerable(Of Object()))
For i = 0 To _SqlSyntaxChecker.QueriedTables.Count - 1
FieldIndexes.Clear()
For j = 0 To _SqlSyntaxChecker.DataFields.Count - 1
If _SqlSyntaxChecker.QueriedTables(i).TableName = _SqlSyntaxChecker.DataFields(j).TableName Then FieldIndexes.Add(_SqlSyntaxChecker.DataFields(j).FieldIndexInDataTable)
Next
Dim query = _SqlSyntaxChecker.QueriedTables(i).Rows.Select(Function(Row) FieldIndexes.Select(Function(FieldIndex) Row.Item(FieldIndex)).ToArray)
Queries.Add(query)
Next
For Each q In Queries
For Each item In q
_OutputDataTable.Rows.Add(item)
Next
Next
But as you see, the result is wrong:
The result should be the same.
What can cause this?
Your query is linked to i field (using closures). But queries are execute after first cycle, so i already have its last value when query is executed.
You can check "Access to modified closure" topic over internet (mainly it's about delegates but actually same thing works for expression trees).

Populate a series of textboxes with random values in Access - run time error 3021 No current record

I'm trying to populate a series of textboxes with random value from a column.
i get the first textbox filled then it returns run time error 3021 - no current record.
i checked the values and the record I'm trying to retrieve doesn't exceed recordcount for the table.
Debug colours rs.move randomrecord.
Dim rs As DAO.Recordset
Dim recordCount As Long
Dim randomRecord As Long
Set rs = CurrentDb.OpenRecordset("SELECT * FROM besede")
rs.MoveLast
rs.MoveFirst
recordCount = rs.recordCount - 1
MsgBox recordCount
Randomize
Dim i As Integer
For i = 1 To 10
randomRecord = Int((recordCount) * Rnd)
rs.Move randomRecord
Controls("t" & i).SetFocus
Controls("t" & i) = rs!test
Next
You are moving the cursor from the current position, so eventually you are trying to read a record at the end of the recordset. Use
rs.MoveFirst before rs.Move randomRecord
to move from the beginning of the recordset.
Check Office Dev Center for more background information on Recordset.Move.

Resources