Token count comparison grammar - antlr

grammar neq;
WS
: [ \t\r\n]+ -> skip
;
root: aGTb | bGTa | eq;
aGTb: 'a'
| eq aGTb
| aGTb eq
| aGTb aGTb
;
bGTa: 'b'
| eq bGTa
| bGTa eq
| bGTa bGTa
;
eq: 'a' 'b'
| 'b' 'a'
| 'a' eq 'b'
| 'b' eq 'a'
| eq eq
;
This version is too slow, however, tests:
Random strings length 20 literals: Avg time =14 ms, Min time =0, Max time =78
Length 40: Avg time =32, Min time =0, Max time =231
Length 60: Avg time =83, Min time =16, Max time =416
Length 80: Avg time =365, Min time =37, Max time =11935
Length 100: Avg time =1442, Min time =67, Max time =25317
Length 120: Avg time =21861, Min time =135, Max time =924769
The input for the above 924 sec parse time:
a b b a a a b b a a b a b a a a a b a a a a b b b a a a a b a a b
a a a b b b a a a b b a b b b a a b b a b a b a b a b a a a a b b
b a a b b b b a a b a b a b a a a b a b b b b b a a a b a a a b b
a b a b a a b b b a a b b a b a a a a a a
Can the grammar be rewritten to faster version? (Be careful, changing say eq into
eq: 'a' 'b'
| 'b' 'a'
| eq 'a' 'b'
| eq 'b' 'a'
| 'a' eq 'b'
| 'b' eq 'a'
| 'a' 'b' eq
| 'b' 'a' eq
;
is not equivalent rule rewriting, as it will not recognize a a b b b b a a).

Related

Combine multiple aggregate queries into one insert statement

I'm trying to make a database with timeseries data and I've got 4 different queries that I'm trying to mash together to form a set of data for a single insert.
SELECT 'some date' AS date, a, b, COUNT(foo) AS c
FROM 'my_db'
WHERE date BETWEEN dateadd(hour,-24,'some date') AND dateadd(hour,-23,'some date')
GROUP BY a, b
ORDER BY a, b ASC;
SELECT a, b, COUNT(foo) AS c1
FROM 'my_db'
WHERE (date BETWEEN dateadd(hour,-24,'some date') AND dateadd(hour,-23,'some date'))
AND (foo = 'some value')
GROUP BY a, b
ORDER BY a, b ASC;
SELECT a, b, COUNT(foo) AS c2
FROM 'my_db'
WHERE (date BETWEEN dateadd(hour,-24,'some date') AND dateadd(hour,-23,'some date'))
AND (foo = 'some other value')
GROUP BY a, b
ORDER BY a, b ASC;
If these were in separate tables, I feel like I'd be able to individually do full outer joins on fields a and b, then just fill the empty fields with 0s.
And by the end, I'd be able to get to the dataset to look like this for a single insert :
| date | a | b | c | c1 | c2 |
--------------------------------------------------------------------
| 2019-08-27 12:00:00 | dog | woof | 100 | 76 | 26 |
| 2019-08-27 12:00:00 | cat | meow | 82 | 33 | 49 |
| 2019-08-27 12:00:00 | pony | neigh | 300 | 0 | 300 |
Is there a clean way to combine these queries into one so that I can bulk insert the formatted set?
Or is there a smarter way to approach the problem?
I think you want conditional aggregation:
SELECT 'some date' AS date, a, b, COUNT(foo) AS c,
SUM(CASE WHEN foo = 'some value' THEN 1 ELSE 0 END) as c1,
SUM(CASE WHEN foo = 'some other value' THEN 1 ELSE 0 END) as c2
FROM 'my_db'
WHERE date BETWEEN dateadd(hour, -24, 'some date') AND dateadd(hour, -23, 'some date')
GROUP BY a, b
ORDER BY a, b ASC;

Unpivot in Potgresql

How can I unpivot in Postgresql without using UNION? I have more than 100 columns, and I am looking for a neat way to do it.
Given table:
id c1 c2 c3
1 X Y Z
2 A B C
3 Y C Z
Desired table:
id col
1 X
1 Y
1 Z
2 A
2 B
2 C
3 Y
3 C
3 Z
Use jsonb functions:
select id, value as col
from my_table
cross join jsonb_each_text(to_jsonb(my_table))
where key <> 'id';
id | value
----+-------
1 | X
1 | Y
1 | Z
2 | A
2 | B
2 | C
3 | Y
3 | C
3 | Z
(9 rows)
Db<>Fiddle.
In Postgres 9.3 or 9.4 use to_json() and json_each_text().
In versions 9.1 or 9.2 install hstore:
create extension if not exists hstore;
select id, value as col
from my_table
cross join each(hstore(my_table))
where key <> 'id';

3 level nested sorting

I'm trying to implement a 3 level nested sort. Basically I have four columns:
A | B | C | D
--- | --- | --- | ---
bob | GOOD| 1 |
kat | BAD | | 24
bob | OK | | 15
bob | GOOD| 20 |
bob | OK | | 10
bob | OK | 5 |
I have three levels of sorting needed...first level Column A ASC, 2nd level is case sorting on column B, and the third level I need to sort based on values from C & D if B = 'GOOD' and sort based on C if B is any other value.
What I currently have is:
ORDER
BY A,
CASE
WHEN B ='GOOD' THEN 1
WHEN B = 'OK' THEN 2
WHEN B = 'BAD' THEN 3
END, C
However this only sorts the third level based on values of C.
You could use another case statement:
ORDER BY A,
CASE B WHEN 'GOOD' THEN 1
WHEN 'OK' THEN 2
WHEN 'BAD' THEN 3
END,
CASE B WHEN 'GOOD' THEN C ELSE D END
In the sample data no row ever seems to have values for both C and D. If this is really the case, you could simplify things using coalesce:
ORDER BY A,
CASE B WHEN 'GOOD' THEN 1
WHEN 'OK' THEN 2
WHEN 'BAD' THEN 3
END,
COALESCE(C, D)
If i got it right
ORDER BY A,
CASE
WHEN B ='GOOD' THEN 1
WHEN B = 'OK' THEN 2
WHEN B = 'BAD' THEN 3
END, C,
CASE WHEN B ='GOOD' THEN D END

Find all possible pairs based on transitivity via T-SQL

I have a concern that doesn't let me go for already several days.
The collective mind is the last resort I may rely on.
Assume we have a table with two columns. Actually, the values are GUIDs, but for the sake of simplicity let's take them as letters.
| a | b |
|---|---|
| x | y |
| y | x |
| y | z |
| z | y |
| m | n |
| m | z |
I need to create a T-SQL query that will present all the possible pairs out of trasitivity, i.e. if x=y, y=z, then x=z. Also, simmetry has to be there, i.e. if there is x=y, then there should be y=x as well.
In this particular case, I believe there is "full house", meaning that every letter is connected to all others through the intermediates. But I need a query that will show that.
All I did is here (SQLFiddle fails to run it):
WITH
t AS
(SELECT 'x' AS a, 'y' AS b
UNION ALL
SELECT 'y' AS a, 'x' AS b
UNION ALL
SELECT 'y' AS a, 'z' AS b
UNION ALL
SELECT 'z' AS a, 'y' AS b
UNION ALL
SELECT 'm' AS a, 'n' AS b
UNION ALL
SELECT 'm' AS a, 'z' AS b),
coupled_reflective AS --for reflective couples we take either of them
(SELECT t2.a, t2.b
FROM t t1
JOIN t t2 ON t1.a=t2.b
AND t1.b!=t2.a),
reversive_coupled_reflective AS --that's another half of the above couples (reversed)
(SELECT t2.b, t2.a
FROM t t1
JOIN t t2 ON t1.a=t2.b
AND t1.b!=t2.a),
rs AS -- reduce the initial set (t)
(SELECT *
FROM coupled_reflective
UNION
SELECT *
FROM t
EXCEPT
SELECT *
FROM reversive_coupled_reflective),
cte AS -- recursively iterate through the set to find transitive values (get linked by the left field)
(SELECT a, b
FROM rs
UNION ALL
SELECT rs.b, cte.b
FROM rs
JOIN cte ON rs.a=cte.a
AND rs.b!=cte.b),
cte2 AS -- recursively iterate through the set to find transitive values (get linked by the right field)
(SELECT a, b
FROM rs
UNION ALL
SELECT rs.a, cte.a
FROM rs
JOIN cte ON rs.b=cte.b
AND rs.a!=cte.a)
SELECT a, b FROM cte2
UNION
SELECT a, b FROM cte
UNION
SELECT a, b FROM t
UNION
SELECT b, a FROM t
But that doesn't do the trick, unfortunately.
The desired result should be
| a | b |
|---|---|
| x | y |
| y | x |
| y | z |
| z | y |
| m | n |
| m | z |
| n | m |
| z | m |
| x | z |
| z | x |
| x | m |
| m | x |
| x | n |
| n | x |
| y | m |
| m | y |
| y | n |
| n | y |
Is there a SQL-gifted buddy out there who can help me here, please?
Thanks.
You can use recursive CTEs, but you need a list of already visited nodes. You can implement that using a string:
with cte as (
select a, b, cast('{' + a + '}{' + b + '}' as varchar(max)) as visited
from t
union all
select cte.a, t.b,
(visited + '{' + t.b + '}')
from cte join
t
on cte.b = t.a
where cte.visited not like '%{' + t.b + '}%'
)
select distinct a, b
from cte;
Note:
The above follows the directed links in the graph. If you want undirected links, then include both:
with t as (
select a, b from yourtable
union
select b, a from yourtable
),
The rest of the logic follows using t.

How to simulate UNPIVOT in Access?

UNPIVOT is available in MS SQL-Server 2005, but AFAIK not in MS Access 2010. How can it be implemented with on-board means? For example, I have a table
ID | A | B | C | Key 1 | Key 2 | Key 3
---------------------------------------
1 | x | y | z | 3 | 199 | 452
2 | x | y | z | 57 | 234 | 452
and want to have a table like
ID | A | B | C | Key
--------------------
1 | x | y | z | 3
2 | x | y | z | 57
1 | x | y | z | 199
2 | x | y | z | 234
2 | x | y | z | 452
Key 452 is a special case. Currently I do the rotation in OLEDB/ATL C++. Although it is fast enough I'm still curious. What is the most efficient SQL statement for Access 2010 here?
This query ...
SELECT ID, A, B, C, [Key 1] AS key_field
FROM tblUnpivotSource
UNION ALL
SELECT ID, A, B, C, [Key 2] AS key_field
FROM tblUnpivotSource
UNION ALL
SELECT ID, A, B, C, [Key 3] AS key_field
FROM tblUnpivotSource;
... returns this recordset (using your sample table values as tblUnpivotSource) ...
ID A B C key_field
-- - - - ---------
1 x y z 3
2 x y z 57
1 x y z 199
2 x y z 234
1 x y z 452
2 x y z 452
Unfortunately there is no easy way to do this with access. You can do this by using a UNION to get each value
SELECT ID, A, B, C, [Key 1] As key
FROM Table
WHERE [Key 1] = 3
UNION ALL
SELECT ID, A, B, C, [Key 1] As key
FROM Table
WHERE [Key 1] = 57
UNION ALL
SELECT ID, A, B, C, [Key 2] As key
FROM Table
WHERE [Key 2] = 199
UNION ALL
SELECT ID, A, B, C, [Key 2] As key
FROM Table
WHERE [Key 2] = 234
UNION ALL
SELECT ID, A, B, C, [Key 3] As key
FROM Table
WHERE [Key 3] = 452
You can create a auxiliary table with all column names as values
(can use excel copy the first row of your table to excel > paste special > transpose)
Create in your table a auto increment column and index this column
Create a new cross join query like the following
SELECT ID, A, B, C
, AUX_TABLE.KEY_FIELD
, DLookUp("[" & [AUX_TABLE].[KEY_FIELD] & "]","TABLE","[ID] = " & [TABLE].[ID]) AS KEY_VALUE
FROM TABLE, AUX_TABLE;
Downside would be you have to maintain AUX_TABLE to keep that working. But if this is a one-time-thing this might be the way to go.