How can we solve this Yacc conflict - conflict

%token A B C D E F G H
%%
x : y H y | z H z
;
y : G | t | y B G | y B t
;
z : w | z D w
;
w : C | t | E z F
;
t : A
;
There is a reduce/reduce conflict because of t. How can we solve it?

By default the conflict between y: t and w: t is resolved in favor of y: t. If you want it the other way, reorder the corresponding rules. Or you can expand one of them like this :-
%token A B C D E F G H
%%
x : y H y | z H z
;
w : C | A | E z F
;
z : w | z D w
;
y : G | A B G | A B A | y B G | y B A
;
Note that I've got rid of the t rule and removed the y: A rule by expanding it :-
Step 1: Eliminate the t: A rule
%token A B C D E F G H
%%
x : y H y | z H z
;
y : G | A | y B G | y B A
;
z : w | z D w
;
w : C | A | E z F
;
Step 2:
%token A B C D E F G H
%%
x : y H y | z H z
;
y : G | A | A B G | A B A | y B G | y B A
;
z : w | z D w
;
w : C | A | E z F
;

Related

How to select unique rows (comparing by a few columns)?

I want to select unique rows from a table (without repeating the combination of 'f' and 'x' fields).
The table:
| f | x | z |
|---|—--|---|
| 1 | 1 | a |
| 1 | 2 | b |
| 1 | 3 | c |
| 1 | 3 | d |
The result:
| f | x | z |
|---|—--|---|
| 1 | 1 | a |
| 1 | 2 | b |
The following query groups rows in "the_table" by "f" and "x", selects the minimum value of "z" in each group and filters out groups with a count greater than 1, returning only unique combinations of "f" and "x".
SELECT f, x, MIN(z) AS z
FROM the_table
GROUP BY f, x
HAVING COUNT(*) = 1;
WITH
check_repetitions AS
(
SELECT
*,
COUNT(*) OVER (PARTITION BY f, x) AS repetitions
FROM
your_table
)
SELECT
f, x, z
FROM
check_repetitions
WHERE
repetitions = 1
You can use the following query to select only rows where the combination of columns f and x do not repeat:
SELECT f, x, MIN(z) AS z
FROM table_name
GROUP BY f, x
HAVING COUNT(*) = 1
This query will group the rows based on the values of f and x, and then return only the rows where the combination of f and x occurs only once. The function MIN is used to select a single value for z for each group.

How to merge different schemas of structs in arrays (filling missing columns with null)?

Given these one tables (a being an array of structs).
baz_v1 (a being ARRAY<STRUCT<x INT64>>):
+===========+
| a.x | b |
+===========+
| 1 | one |
| 2 | |
+===========+
baz_v2 (a being ARRAY<STRUCT<x INT64, y INT64>>):
+=================+
| a.x | a.z | b |
+=================+
| 3 | 4 | one |
| 5 | 6 | |
+-----------------+
| 7 | 8 | two |
| 9 | 0 | |
+-----------------+
| 11 | 12 | two |
| 13 | 14 | |
+=================+
How can I obtain the following (concatenated) table/view?
+==================+
| a.x | a.y | b |
+==================+
| 1 | null | one |
| 2 | null | |
+------------------+
| 3 | 4 | one |
| 5 | 6 | |
+------------------+
| 7 | 8 | two |
| 9 | 10 | |
+------------------+
| 11 | 12 | two |
| 13 | 14 | |
+==================+
Code:
WITH `baz_v1` AS (
SELECT
[
STRUCT(1 AS x),
STRUCT(2 AS x)
]
a,
"one" b
), `baz_v2` AS (
SELECT
[
STRUCT(3 AS x, 4 AS y),
STRUCT(5 AS x, 6 AS y)
]
a,
"one" b
UNION ALL
SELECT
[
STRUCT(7 AS x, 8 AS y),
STRUCT(9 AS x, 10 AS y)
]
a,
"two" b
UNION ALL
SELECT
[
STRUCT(11 AS x, 12 AS y),
STRUCT(13 AS x, 14 AS y)
]
a,
"two" b
)
-- todo: Insert magic here, because the below, of course, does not work.
SELECT * FROM baz_v2
UNION ALL
SELECT * FROM baz_v1
Consider below
select * replace(
array(select as struct x, null as y from t.a) as a)
from `baz_v1` t
union all
select * from `baz_v2`
if applied to sample data in y our question - output is
Building on the very good answer given by Mikhail Berlyant, I've found another solution, one that does not use SELECT * REPLACE:
SELECT ARRAY(SELECT AS STRUCT x, NULL AS y FROM baz_v1.a) AS a, b FROM baz_v1
UNION ALL
SELECT * FROM baz_v2
The following method was used to merge these tables.
(1) To use an union all the target tables, array type was flattened.
(2) In order to match the number of columns in the UNION ALL target tables, the number of columns is appended as much as the insufficient number of columns by
LEFT JOIN (SELECT '' as y) ON FALSE - reference
(3) ARRAY_AGG, STRUCT, GROUP BY were used to output the array result. - reference
WITH `baz_v1` AS (
SELECT
[
STRUCT(1 AS x),
STRUCT(2 AS x)
] a,
"one" b
), `baz_v2` AS (
SELECT
[
STRUCT(3 AS x, 4 AS y),
STRUCT(5 AS x, 6 AS y)
] a,
"two" b
)
SELECT ARRAY_AGG (
STRUCT(x, y)
) as a,
b
FROM (
SELECT xy.x as x, xy.y as y, b
FROM baz_v2, UNNEST(a) as xy
UNION ALL
SELECT x, y, b
FROM (
SELECT x.x as x, CAST(y as INT64) as y, b
FROM baz_v1, UNNEST(a) as x
LEFT JOIN (SELECT '' as y) ON FALSE
)
)
GROUP BY b
with result)

SQL Server Pivot Assistance

SQL Server. It may be solved by using Pivot.
I have data (all are in string):
X Y Z ---Heading
A a p
A b q
A c r
B a s
B b t
B c u
I want output:
a b c ---Heading
A p q r
B s t u
You can try using case expression. here is the demo.
select
X,
max(case when Y = 'a' then Z end) as a,
max(case when Y = 'b' then Z end) as b,
max(case when Y = 'c' then Z end) as c
from myTable
group by
X
order by
X;
output:
| x | a | b | c |
| --- | --- | --- | --- |
| A | p | q | r |
| B | s | t | u |

Partially meeting conditions and sorting by match rank

Given a table where very simplified data looks like the following (but it could include millions of rows with a lot more data in dozens of columns of different types):
+----+----+---+-----+
| ID | X | Y | Z |
+----+----+---+-----+
| 1 | 1 | 1 | "a" |
| 2 | 1 | 0 | "a" |
| 3 | 0 | 1 | "a" |
| 4 | 0 | 0 | "a" |
| 5 | 0 | 0 | "b" |
+----+----+---+-----+
What would be the approach to select only the data with full AND MAYBE partial condition matching but up to a certain match rank, with the results sorted by that match rank?
E.g. when the condition is WHERE ((X = 1) AND (Y = 1) AND (Z = "a")) how would it be possible to get the following results in the following order:
+----+----+---+-----+-------+
| ID | X | Y | Z | MATCH |
+----+----+---+-----+-------+
| 1 | 1 | 1 | "a" | 100% | <- 100% because all conditions matched
| 2 | 1 | 0 | "a" | 66% | <- 66% because X & Z matched but Y didn't
| 3 | 0 | 1 | "a" | 66% | <- 66% because Y & Z matched but X didn't
| 4 | 0 | 0 | "a" | 33% | <- 33% because Z matched but X & Y didn't
| 5 | 0 | 0 | "b" | 0% | <- 0% because nothing matched
+----+----+---+-----+-------+
Or being able to select up to a certain match rank, so with WHERE ((X = 1) AND (Y = 1) AND (Z = "a")) AND (MATCH >= 25) we'd only get the following:
+----+----+---+-----+-------+
| ID | X | Y | Z | MATCH |
+----+----+---+-----+-------+
| 1 | 1 | 1 | "a" | 100% |
| 2 | 1 | 0 | "a" | 66% |
| 3 | 0 | 1 | "a" | 66% |
| 4 | 0 | 0 | "a" | 33% |
+----+----+---+-----+-------+
Or with WHERE ((X = 1) AND (Y = 1) AND (Z = "a")) AND (MATCH >= 75) to get:
+----+----+---+-----+-------+
| ID | X | Y | Z | MATCH |
+----+----+---+-----+-------+
| 1 | 1 | 1 | "a" | 100% |
+----+----+---+-----+-------+
Due to the table having tens of millions of rows iterating over them wouldn't be possible for scalability reasons (but other required conditions could be passed to narrow down the results).
The percentage values are for illustrative purposes only and aren't strictly required (the same applies for the looks of the MATCH >= XX% condition which would likely have to be represented differently).
I guess I'm looking for something like this
SELECT *
FROM xyz
WHERE (X = 1 AND Y = 1 AND Z = "a")
OR (X != 1 AND Y = 1 AND Z = "a")
OR (X = 1 AND Y != 1 AND Z = "a")
OR (X = 1 AND Y = 1 AND Z != "a")
OR (X = 1 AND Y != 1 AND Z != "a")
OR (X != 1 AND Y != 1 AND Z = "a")
OR (X != 1 AND Y = 1 AND Z != "a")
OR (X != 1 AND Y != 1 AND Z != "a")
But it of course wouldn't necessarily sort them in the order of the match rank nor allow specifying the match rank (other than maybe programmatically generating the needed number of OR conditions which is also an option).
This answers the original version of the question.
You can do the calculation in-line:
select t.*
from (select x, y,
((x = ?)::int + (y = ?)::int) / 2.0 as match
from t
) t
where match = ?;
The ? are placeholders for your values.
I can think of one way using JSONB to count the number of matches:
with vals (x,y,z) as (
values (1, 1,'a')
)
select d.*,
(select count(*)
from (
select jsonb_build_object(k,v)
from jsonb_each(to_jsonb(v)) as t1(k,v)
intersect
select jsonb_build_object(k,v)
from jsonb_each(to_jsonb(d) - 'id') as t2(k,v)
)t
) as num_matches
from data d
cross join vals v
where d.x = v.x
or d.y = v.y
or d.z = v.z
order by num_matches desc;
Not very pretty but at least the calculation of the number of matches is dynamic based on the number of columns of the "values" part.
returns:
id | x | y | z | num_matches
---+---+---+---+------------
1 | 1 | 1 | a | 3
2 | 1 | 0 | a | 2
3 | 0 | 1 | a | 2
4 | 0 | 0 | a | 1
If there are more columns that need to be ignored (not just id), you need to extend the to_jsonb(d) - 'id' to also remove the other columns - which makes this only partially "dynamic".
Doing this and calculating the percentage can all be put into a function:
create or replace function match_percent(p_values jsonb, p_row data)
returns int
as
$$
select ((count(*)::numeric / (select count(*) from jsonb_object_keys(p_values)))*100)::int
from (
select jsonb_build_object(k,v)
from jsonb_each(p_values) as t1(k,v)
intersect
select jsonb_build_object(k,v)
from jsonb_each(to_jsonb(p_row)) as t2(k,v)
where t2.k in (select k from jsonb_object_keys(p_values))
) x;
$$
language sql
stable;
Then the query can be simplified to:
with vals (x,y,z) as (
values (1, 1,'a')
)
select d.*,
match_percent(to_jsonb(v), d)
from data d
cross join vals v
where d.x = v.x
or d.y = v.y
or d.z = v.z
order by match_percent desc;

SQL Server : most frequent value in each row

How can I find most frequent value in each row in SQL Server?
Example:
1 a d a a c a b --> a
2 b a c b b b d --> b
3 h a h h b c d --> h
4 d d c h g p m --> d
5 e e g h d e h --> e
In first row, 'a' is most frequent value, etc.
Considering these values are in separate columns, with an UNPIVOT query the solution would look something like.....
Test Data
Declare #T table (ID INT , Col1 varchar(1) , Col2 varchar(1) , Col3 varchar(1)
, Col4 varchar(1) , Col5 varchar(1) , Col6 varchar(1) , Col7 varchar(1))
Insert Into #T values
('1','a','d','a','a','c','a','b'),
('2','b','a','c','b','b','b','d'),
('3','h','a','h','h','b','c','d'),
('4','d','d','c','h','g','p','m'),
('5','e','e','g','h','d','e','h');
Query
WITH X AS (
Select ID , Val, COUNT(*)total
,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY COUNT(*) DESC) rn
from #T
UNPIVOT (Val FOR N IN (Col1,Col2,Col3,Col4,Col5,Col6,Col7))up
GROUP BY ID , Val
)
Select t.* , Val
FROM X
INNER JOIN #T t ON x.ID = t.ID
WHERE rn = 1
Result Set
+----+------+------+------+------+------+------+------+-----+
| ID | Col1 | Col2 | Col3 | Col4 | Col5 | Col6 | Col7 | Val |
+----+------+------+------+------+------+------+------+-----+
| 1 | a | d | a | a | c | a | b | a |
| 2 | b | a | c | b | b | b | d | b |
| 3 | h | a | h | h | b | c | d | h |
| 4 | d | d | c | h | g | p | m | d |
| 5 | e | e | g | h | d | e | h | e |
+----+------+------+------+------+------+------+------+-----+