KDB/Q Question on Parsing a string and concatenate - kdb

InstanceID
OrderID
Strategy
Fills
NG1
jhbubuy-ClientName1-2022-07-01
VWAP
5000
NG2
nb8yvce-ClientName2-2022-07-01
POV
300
NG1
cebciube-ClientName3-2022-07-01
TWAP
1000
Hi guys have the table above as an example. I am beginner in KDB/Q so please bear with me. Any guidance would help.
1.) I am trying to parse OrderID by "-" and create a new column "ClientName" based on what I parsed on OrderID
InstanceID
OrderID
Strategy
Fills
ClientName
NG1
jhbubuy-ClientName1-2022-07-01
VWAP
5000
ClientName1
NG2
nb8yvce-ClientName124-2022-07-01
POV
300
ClientName124
NG1
cebciube-ClientNameABC-2022-07-01
TWAP
1000
ClientNameABC
2.) Create a new column by parsing OrderID then concatenate it with a separate column called InstanceID. So it creates a InstanceID_OrderID
InstanceID
OrderID
Strategy
Fills
InstanceID_OrderID
NG1
jhbubuy-ClientName1-2022-07-01
VWAP
5000
NG1-jhbubuy-2022-07-01
NG2
nb8yvce-ClientName124-2022-07-01
POV
300
NG2-nb8yvce-ClientName124-2022-07-01
NG1
cebciube-ClientNameABC-2022-07-01
TWAP
1000
NG1-ClientNameABC-2022-07-01

To get desired table you should:
Use vs operator to split OrderID by "-"
Get second element of each sublist for ClientName
Remove second element from each sublist, prepend InstanceID and join new list using sv operator for InstanceID_OrderID.
If the table has following structure:
t: flip`InstanceID`OrderID`Strategy`Fills!
(`NG1`NG2`NG3;
("hbubuy-ClientName1-2022-07-01";
"nb8yvce-ClientName2-2022-07-01";
"cebciube-ClientName3-2022-07-01");
`VWAP`POV`TWAP;
5000 300 1000);
The next query does the job:
update
ClientName: {("-" vs x) 1} each OrderID,
InstanceID_OrderID: {x: "-" vs x; "-" sv enlist[y],(1#x),2_x}'[OrderID;string InstanceID]
from t
where
anonymous function {("-" vs x) 1} splits OrderID by "-" and chooses index 1
{x: "-" vs x; "-" sv enlist[y],(1#x),2_x} splits OrderID by "-", chooses 0 and 2+ indices (there is no remove by index operation in Q), prepends InstanceID and joins everything into "-" delimited string

Related

Need help in re-order / sort Bars in Bar Chart in Qliksense

I am trying to reorder the Bars in bar chart in qliksense. Below is the order which I have to sort the bars.
NEW
IN_PROGRESS_PL
ASSIGNED
IN_PROGRESS_SPOC
RESOLVED/CLOSED
FBS E2S BACKLOG
CANCELLED
DUPLICATE
But by default the bars are ordered in alphabetical order or by the numbers, I have tried the below solution, but it still didn't rearranged the bars.
Default order :
Match('NEW','IN_PROGRESS_PL','ASSIGNED','IN_PROGRESS_SPOC','RESOLVED/CLOSED','FBS E2S BACKLOG' , 'CANCELLED','DUPLICATE' )
Can someone please help me to fix this issue.
The first parameter in Match is the targeted field.
If we have data like:
RawData:
Load * inline [
Stage , IdeasCount
ASSIGNED , 63
CANCELLED , 11
INTERNAL_123, 2
IN_PROGRESS1, 20
IN_PROGRESS2, 47
];
Then the sorting expression will be:
=match(Stage, 'IN_PROGRESS1', 'ASSIGNED', 'IN_PROGRESS2', 'INTERNAL_123', 'CANCELLED')
The sorting properties:
And the result chart will have the correct sorting:
P.S. (1)
Have to mention that if the value is not found in the Match function then the function will return null and thus the corresponding bar will be pushed in the front. In this case you can wrap the match in if statement and if no matching value is found then assign some large number:
= if( match(Field, 'value1', 'value2' ...) > 0,
match(Field, 'value1', 'value2' ...),
1000
)
P.S. (2)
In your case you can also use Dual function to create "default" sort order for a specific field.
If we change the script to:
RawData:
Load
Dual(Stage, OrderId) as Stage,
IdeasCount
;
Load * inline [
Stage , IdeasCount, OrderId
ASSIGNED , 63 , 2
CANCELLED , 11 , 5
INTERNAL_123, 2 , 4
IN_PROGRESS1, 20 , 1
IN_PROGRESS2, 47 , 3
]
;
The result table will look the same - with two fields only Stage and IdeasCount. But in the background each value of Stage field will have and number representation (based on the initial OrderId field). And as a side effect of that the auto sorting option will sort the field data by its internal number representation and the chart will "sort itself" correctly

How to add blank lines to complete an Access report so that it matches a printed form?

I deal with a bunch of government forms, and I constantly find myself trying to get Access 2013 to output a report that matches a pre-printed form.
Most of the forms are now in .pdf form, so Access doesn't handle them well (to my knowledge) without additional software, which I cannot install on user production computers.
So I usually re-create the form as an Access report, but I have real difficulty when I have only enough records for a half a page.
How do I get a report to print the required records, and then fill the page with blank records so the "form" looks correct?
I'd be willing to try any possible solution - I've even gone so far as to create blank records to try to make this work. My goal, however, is to automate this process so any user can generate the report and it prints out correctly without a bunch of fiddling.
On this form, some or all of the lines might be used, and each person (I have ~550 people who each have an individual form) has a different number of lines, depending on the number of jumps they have completed.
I would have a dummy table with just a single numeric field called id. Populate this with a number of records greater than the biggest number of 'extra' records you're ever going to need to fill your form, using numbers from 1 upwards for id. Now say your original record source query for the report is:
select field1, field2, field3, field4
from myTable
order by field1
and you always want 15 rows to fill your form, then change the report's record source to:
select top 15 sort_seq, field1, field2, field3, field4
from (
select 0 as sort_seq, field1, field2, field3, field4
from myTable
union
select id, null, null, null, null
from dummyTable
) as X
order by sort_seq, field1
Ok, so you are recreating the complete PDF form as Access report. And the lower part is the details section of a continuous report.
The easiest way is indeed to create the correct number of empty records, but do this automatically (e.g. using DAO Recordset.AddNew).
I like to use a separate dummy table for this (with the same structure as the original table) to not pollute the original table with dummy records.
The form has 20 lines, so if your original table has 7 records for the current person, insert 13 records into the dummy table, using a large PK so they get sorted to the bottom.
Then use a query as record source for the report, joining the master table with a UNION of Jumps and Dummy table.
Alternatively there is the Report.Line() method: https://msdn.microsoft.com/en-us/library/office/ff198297.aspx
But this will be a lot more complicated to get it right.
I hope you have found the solution to this issue. I am posting this in the hope that if someone else finds this same question they will find this one as the best possible solution, as I have been struggling for about 2 days now.
I suppose you have at least two tables and a query where you mix information from both, and your report is made out from this query. I will call this your Query1, so the trick is that when you open the Report from your Form, you will use the label number of your Form to make the filter of the Query1 from where you will get the information for your report. Like this:
rectotal = DCount("*", "Quote-Report", "[Quote_No]=" & stQtNo)
This instruction will count how many records you have on your “Quote-Report” Query that are of the same value of the [Quote_No] field inside your Quote Form , as you are assigning the stQtNo variable to the Form with the name “Quote”; and put this value on your rectotal variable.
Now with rectotal you need to determine how many lines you want in your report. I used 28 so the following formula will give how many blank rows needed to fill the page.
If rectotal / 28 - Int(rectotal / 28) = 0 Then
x = 28
Else
x = 28 * (rectotal / 28 - Int(rectotal / 28))
End If
blanklines = 28 - x
tLines = rectotal + blanklines
Of course you can use 15, 20 or as many rows you want your report to print per page. The variable blanklines will give you this number.
Then I use the recordset procedure from Access to open the table where I have the information that will print in the detail section of the report and fill with as many records as I need using the blanklines variable and the Do Until Loop.
Set db = CurrentDb
Set rs = db.OpenRecordset("Item_QD", dbOpenDynaset, dbseechanges)
Item_QD is the table
where I want to insert new records to fill the report detail section.
x = 0
rs.MoveLast
Do Until x = blanklines
rs.AddNew
rs![Quote_No] = stQtNo
rs![CODE No] = "Z" & x I use the Z and the number so I can erase the records once I don’t need them anymore.
x = x + 1
rs.Update
Loop
Please note that you need to Dim db As Database and Dim rs As Recordset variables outside your private sub in module header. Also your Access needs to be running these two classes so you can use them.
Finally once you open your report you have to go to your detail section and do a requery command to refresh all the rows prior to print. Need to format your detail cells in order to print the grid line and the rest is history.
The beauty of this procedure is that you can add and handle as many rows as you want or need inside your report and make a professional look of your quotes, packing slips, purchase orders, etc.
Hope this solves many problems. For a fast reference on how to use and manage ACCESS record set visit: https://www.youtube.com/watch?v=WNm17l54z1c
Private Sub Report_Open(Cancel As Integer)
Dim blanklines, tLines As Integer
Dim rectotal, x As Long
Dim stQtNo As String
Set db = CurrentDb
Set rs = db.OpenRecordset("Item_QD", dbOpenDynaset, dbseechanges)
stQtNo = Forms![Quote].[Quote_No]
rectotal = DCount("*", "Quote-Report", "[Quote_No]=" & stQtNo)
If rectotal / 28 - Int(rectotal / 28) = 0 Then
x = 28
Else
x = 28 * (rectotal / 28 - Int(rectotal / 28))
End If
blanklines = 28 - x
tLines = rectotal + blanklines
x = 0
rs.MoveLast
Do Until x = blanklines
rs.AddNew
rs![Quote_No] = stQtNo
rs![CODE No] = "Z" & x
x = x + 1
rs.Update
Loop
End Sub
Another solution. Code behind report. I use it for a simple report without grouping and row height doesn't change.
Option Compare Database
Option Explicit
Public totalCount As Integer
Private Sub Detail_Print(Cancel As Integer, PrintCount As Integer)
printBlankRecords getnumberofBlanks
End Sub
Public Sub printBlankRecords(numberBlanks As Integer)
Dim recordCount As Integer
recordCount = DCount("*", Me.RecordSource)
totalCount = totalCount + 1
If totalCount = recordCount Then
Me.NextRecord = False
'once you get to the last record, stay on last record
ElseIf totalCount > recordCount And totalCount < (recordCount + numberBlanks) Then
Me.NextRecord = False
'make the font and backcolor the same appearing to be empty record for each field
Me.RateID.ForeColor = Me.RateID.BackColor
End If
End Sub
Public Function getnumberofBlanks() As Integer
Dim recordCount As Integer
Dim N As Integer, X As Integer
X = 20
recordCount = DCount("*", Me.RecordSource)
If recordCount <= X Then
getnumberofBlanks = X - recordCount
Else
N = 1
Do While N * 21 + X <= recordCount
N = N + 1
Loop
getnumberofBlanks = (N * 21 + X) - recordCount
End If
End Function

Get substring into a new column

I have a table that contains a column that has data in the following format - lets call the column "title" and the table "s"
title
ab.123
ab.321
cde.456
cde.654
fghi.789
fghi.987
I am trying to get a unique list of the characters that come before the "." so that i end up with this:
ab
cde
fghi
I have tried selecting the initial column into a table then trying to do an update to create a new column that is the position of the dot using "ss".
something like this:
t: select title from s
update thedot: (title ss `.)[0] from t
i was then going to try and do a 3rd column that would be "N" number of characters from "title" where N is the value stored in "thedot" column.
All i get when i try the update is a "type" error.
Any ideas? I am very new to kdb so no doubt doing something simple in a very silly way.
the reason why you get the type error is because ss only works on string type, not symbol. Plus ss is not vector based function so you need to combine it with each '.
q)update thedot:string[title] ss' "." from t
title thedot
---------------
ab.123 2
ab.321 2
cde.456 3
cde.654 3
fghi.789 4
There are a few ways to solve your problem:
q)select distinct(`$"." vs' string title)[;0] from t
x
----
ab
cde
fghi
q)select distinct(` vs' title)[;0] from t
x
----
ab
cde
fghi
You can read here for more info: http://code.kx.com/q/ref/casting/#vs
An alternative is to make use of the 0: operator, to parse around the "." delimiter. This operator is especially useful if you have a fixed number of 'columns' like in a csv file. In this case where there is a fixed number of columns and we only want the first, a list of distinct characters before the "." can be returned with:
exec distinct raze("S ";".")0:string title from t
`ab`cde`fghi
OR:
distinct raze("S ";".")0:string t`title
`ab`cde`fghi
Where "S " defines the types of each column and "." is the record delimiter. For records with differing number of columns it would be better to use the vs operator.
A variation of WooiKent's answer using each-right (/:) :
q)exec distinct (` vs/:x)[;0] from t
`ab`cde`fghi

getting categoryid fo more than one shortname passed

I have the following tables:
business
id catid subcatid
---------------------
10 {1} {10,20}
20 {2} {30,40}
30 {3} {50,60,70}
cat_subcat
catid shortname parent_id bid
--------------------------------------------
1 A 10
2 B 20
3 c 30
10 x 1 10
20 y 1 10
30 z 2 20
40 w 2 20
Both the tables have a relationship using id. The problem I am getting is outlined below. Here is my query currently:
SELECT ARRAY[category_id]::int[] from cat_subcat
where parentcategoryid IS not NULL and shortname ilike ('x,y');
I want to get the category_id for an entered shortname, but my query is not giving the proper output. If I pass one shortname it will retrieve the category_id, but if I pass more than one shortname it will not display category_id. Please tell me how to get the category_id for more than one shortname passed.
To actually use pattern matching with ILIKE, you cannot use a simple IN expression. Instead, you need ILIKE ANY (...) or ALL (...), depending on whether you want the tests ORed or ANDed:
Also, your ARRAY constructor will be applied to individual rows, which seems rather pointless. I assume you want this instead (educated guess):
SELECT array_agg(catid) AS cats
FROM cat_subcat
WHERE parent_id IS NOT NULL
AND shortname ILIKE ANY ('{x,y}');
Well, as long as you don't use wildcards (%, _) for your pattern, you can translate this to:
AND lower(shortname) IN ('x','y');
But that would be rather pointless, since Postgres internally converts this to:
AND lower(shortname) = ANY ('{x,y}');
.. before evaluating.

Perl + PostgreSQL-- Selective Column to Row Transpose

I'm trying to find a way to use Perl to further process a PostgreSQL output. If there's a better way to do this via PostgreSQL, please let me know. I basically need to choose certain columns (Realtime, Value) in a file to concatenate certains columns to create a row while keeping ID and CAT.
First time posting, so please let me know if I missed anything.
Input:
ID CAT Realtime Value
A 1 time1 55
A 1 time2 57
B 1 time3 75
C 2 time4 60
C 3 time5 66
C 3 time6 67
Output:
ID CAT Time Values
A 1 time 1,time2 55,57
B 1 time3 75
C 2 time4 60
C 3 time5,time6 66,67
You could do this most simply in Postgres like so (using array columns)
CREATE TEMP TABLE output AS SELECT
id, cat, ARRAY_AGG(realtime) as time, ARRAY_AGG(value) as values
FROM input GROUP BY id, cat;
Then select whatever you want out of the output table.
SELECT id
, cat
, string_agg(realtime, ',') AS realtimes
, string_agg(value, ',') AS values
FROM input
GROUP BY 1, 2
ORDER BY 1, 2;
string_agg() requires PostgreSQL 9.0 or later and concatenates all values to a delimiter-separated string - while array_agg() (v8.4+) creates am array out of the input values.
About 1, 2 - I quote the manual on the SELECT command:
GROUP BY clause
expression can be an input column name, or the name or ordinal number
of an output column (SELECT list item), or ...
ORDER BY clause
Each expression can be the name or ordinal number of an output column
(SELECT list item), or
Emphasis mine. So that's just notational convenience. Especially handy with complex expressions in the SELECT list.