I've written a predicate called solve_crossword that looks like this:
solve_crossword(X,C):-
C= [A1,A2,A3,A4,A5,
B1,' ',B3, ' ', B5,
C1, C2,C3,C4,C5,
D1,' ',D3,' ', D5,
E1,E2,E3,E4,E5],
member([A1, A2, A3, A4, A5], X),
member([C1, C2, C3, C4, C5], X),
member([E1, E2, E3, E4, E5], X),
member([A1, B1, C1, D1, E1], X),
member([A3, B3, C3, D3, E3], X),
member([A5, B5, C5, D5, E5], X).
Now, I want to write a predicate called write_crossword that formats the crossword. If I have a list of words I want it to look like this:
| ?- words(X), solve_crossword(X, C), write_crossword(C).
DITCH
O U O
DITTO
G O E
EARLY
C = [[68,73,84,67,72],[79,32,85,32,79],...
X = [[68,73,83,84,82],[68,73,84,67,72],...
With
words([
"DISTR",
"DITCH",
"DITTO",
"DITTY",
"DODGE",
"EARED",
"EARLY",
"EARTH",
"EASEL",
"HONOR",
"HOOEY",
"HORDE",
"TUQUE",
"TURPS",
"TUTOR",
"TWAIN"
]).
Rows 1, 3, 5 and columns 1, 3, 5 are supposed to be words.
You can try something like this (note I corrected your code for solve_crossword):
solve_crossword(X,C):-
C= [[A1,A2,A3,A4,A5],
[B1,Space,B3, Space,B5],
[C1,C2,C3,C4,C5],
[D1,Space,D3,Space,D5],
[E1,E2,E3,E4,E5]],
atom_codes(' ', [Space]),
member([A1, A2, A3, A4, A5], X),
member([C1, C2, C3, C4, C5], X),
member([E1, E2, E3, E4, E5], X),
member([A1, B1, C1, D1, E1], X),
member([A3, B3, C3, D3, E3], X),
member([A5, B5, C5, D5, E5], X).
write_crossword([]).
write_crossword([Line|Lines]):-
atom_codes(SLine, Line),
write(SLine),
nl,
write_crossword(Lines).
atom_codes/2 converts between an atom and a list of character codes.
Related
I have an ORACLE SQL query that is as follows :
SELECT B1 "Categories",
B2 "Category1",
B3 "Category2",
B4 "Category3",
B5 "Category4",
B6 "Total"
FROM (
SELECT C_NAME B1,
SUM(BUCKET_1) B2,
SUM(BUCKET_2) B3,
SUM(BUCKET_3) B4,
SUM(BUCKET_4) + SUM(BUCKET_5) + SUM(BUCKET_6) B5,
SUM(BUCKET) B6
FROM BUCKETTABLE
WHERE ID = bucketID
GROUP BY C_NAME
ORDER BY C_NAME
);
The result I get from the query :
The result I want to obtain :
How do I get this result?
I have a table with 60+ columns in it that I would like to UNPIVOT so that each column becomes a row and then find the fill rate, min value and max value of each entry.
For Example
ID
START_DATE
END_DATE
EVENT_ID
PROVIDER_CODE
01
01/23/21
03/14/21
0023401
0012323
02
06/04/21
09/20/21
0025906
0023454
03
07/20/21
12/02/21
0027093
0034983
And I want the output to look like
Column_Name
Fill_Rate
Min
Max
ID
0.7934
01
03
Start_Date
0.6990
01/23/21
07/20/21
End_Date
0.9089
03/14/21
12/02/21
Event_ID
1.0000
0023401
0027093
Struggling to get the desired output, especially because of different data types in the different columns
i tried doing the following, but it doesn't allow taking the agg functions within the unpivot
select *
from "DSVC_MERCKPAN_PROD"."COHORTS_LATEST"."MEDICAL_HEADERS"
UNPIVOT (
max(code) as max_value,
min(code) as min_value,
avg(code) as fill_rate,
code as column_name
)
For fill rate, I was trying to use this logic as ID is always populated so it has the total number of rows, however the other columns can be null
(COUNT_IF(start_date is not null))/(COUNT_IF(ID is not null))) as FILL_RATE,
I have 2 ideas to implement the report.
The first way is casting all values to VARCHAR and then using UNPIVOT:
-- Generate dummy data
create or replace table t1 (c1 int, c2 int, c3 int, c4 int, c5 int, c6 int, c7 int, c8 int, c9 int, c10 int) as
select
iff(random()%2=0, random(), null), iff(random()%2=0, random(), null),
iff(random()%2=0, random(), null), iff(random()%2=0, random(), null),
iff(random()%2=0, random(), null), iff(random()%2=0, random(), null),
iff(random()%2=0, random(), null), iff(random()%2=0, random(), null),
iff(random()%2=0, random(), null), iff(random()%2=0, random(), null)
from table(generator(rowcount => 1000000000))
;
-- Query
with
cols as (
select column_name, ordinal_position
from information_schema.columns
where table_catalog = current_database()
and table_schema = current_schema()
and table_name = 'T1'
),
stringified as (
select
c1::varchar c1, c2::varchar c2, c3::varchar c3, c4::varchar c4, c5::varchar c5,
c6::varchar c6, c7::varchar c7, c8::varchar c8, c9::varchar c9, c10::varchar c10
from t1
),
data as (
select column_name, column_value
from stringified
unpivot(column_value for column_name in (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10))
)
select
c.column_name,
count(d.column_value)/(select count(*) from t1) fill_rate,
min(d.column_value) min,
max(d.column_value) max
from cols c
left join data d using (column_name)
group by c.column_name, c.ordinal_position
order by c.ordinal_position
;
/*
COLUMN_NAME FILL_RATE MIN MAX
C1 0.500000 -1000000069270747870 999999972962694409
C2 0.499980 -1000000027928146782 999999946877079818
C3 0.499996 -1000000012155323098 999999942281548701
C4 0.500017 -1000000056353213091 999999946421698482
C5 0.500015 -1000000015608859996 999999993977648967
C6 0.500003 -1000000007081089270 999999998851014730
C7 0.499987 -100000008605944993 999999968272328033
C8 0.499992 -1000000042470913027 999999977402822725
C9 0.500011 -1000000058928465662 999999969060696774
C10 0.500029 -1000000011306371004 99999996061390938
*/
It's a straightforward way, but it still needs to list up all column names twice and it's a bit tough in the case the number of columns is very massive (but I believe it's much better than a huge UNION ALL query).
Another solution is a bit tricky, but you can unpivot a table by using OBJECT_CONSTRUCT(*) aggregation if the row length doesn't exceed a VARIANT value limit (16 MiB):
with
cols as (
select column_name, ordinal_position
from information_schema.columns
where table_catalog = current_database()
and table_schema = current_schema()
and table_name = 'T1'
),
data as (
select f.key column_name, f.value::varchar column_value
from (select object_construct(*) rec from t1) up,
lateral flatten(up.rec) f
)
select
c.column_name,
count(d.column_value)/(select count(*) from t1) fill_rate,
min(d.column_value) min,
max(d.column_value) max
from cols c
left join data d using (column_name)
group by c.column_name, c.ordinal_position
order by c.ordinal_position
;
/*
COLUMN_NAME FILL_RATE MIN MAX
C1 0.500000 -1000000069270747870 999999972962694409
C2 0.499980 -1000000027928146782 999999946877079818
C3 0.499996 -1000000012155323098 999999942281548701
C4 0.500017 -1000000056353213091 999999946421698482
C5 0.500015 -1000000015608859996 999999993977648967
C6 0.500003 -1000000007081089270 999999998851014730
C7 0.499987 -100000008605944993 999999968272328033
C8 0.499992 -1000000042470913027 999999977402822725
C9 0.500011 -1000000058928465662 999999969060696774
C10 0.500029 -1000000011306371004 99999996061390938
*/
OBJECT_CONSTRUCT(*) aggregation is a special usage of the OBJECT_CONSTRUCT function that extracts column names as a key of each JSON object. As far as I know, this is the only way to extract column names from a table along with values in a programmatic way.
Since OBJECT_CONSTRUCT is relatively a heavy operation, it usually takes a longer time than the first solution, but you don't need to write all column names with this trick.
Is this something I can do? I'm looking to try and cross-correlate two time series.
I think the window function should do it, but not 100% sure how to construct it in SQL.
Just as an idea to play with (more suited for comments but easier to present in answer):
SELECT
CORR(a, b) AS correlation,
CORR(a, b1) AS cross_correlation_lag_1,
CORR(a, b2) AS cross_correlation_lag_2,
CORR(a, b3) AS cross_correlation_lag_3,
CORR(a, b4) AS cross_correlation_lag_4,
CORR(a, b5) AS cross_correlation_lag_5
FROM (
SELECT
a.ts AS ts,
a.val AS a,
b.val AS b,
LEAD(b.val, 1) OVER(ORDER BY b.ts) AS b1,
LEAD(b.val, 2) OVER(ORDER BY b.ts) AS b2,
LEAD(b.val, 3) OVER(ORDER BY b.ts) AS b3,
LEAD(b.val, 4) OVER(ORDER BY b.ts) AS b4,
LEAD(b.val, 5) OVER(ORDER BY b.ts) AS b5
FROM time_series1 AS a
JOIN time_series2 AS b
ON a.ts = b.ts
)
My understanding was that most OR conditions can be replaced where performance benefits with UNION ALL. But for these 2 queries the Count ('1') is not the same. Why is it so- am I missing something here. Should it not be the same . Can someone explain the disparity
SQL # 1
sel
D1.COL_1_CD, D1.COL_1_DESC,
D2.COL_2_CD, D2.COL_2_DESC, D3.COL_3_CD, D3.COL_3_DESC, D4.COL_4_CD,
D4.COL_4_DESC, D5.COL_5_CD, D5.COL_5_DESC,
d1.COL_1_CD_SYS_ID,
d2.COL_2_CD_SYS_ID,
d3.COL_3_CD_SYS_ID,
d4.COL_4_CD_SYS_ID,
d5.COL_5_CD_SYS_ID
from
D1,
D2, D3, D4,
D5
where
D1.COL_1_CD1=D2.COL_2_CD1
and
D2.COL_2_CD1=D3.COL_3_CD1
and
D4.COL_4_CD1=D5.COL_5_CD1
and
(D1.COL_1_CD in ('707')
or D2.COL_2_CD in ('707')
or D3.COL_3_CD in ('707')
or D4.COL_4_CD in ('707')
or D5.COL_5_CD in ('707') )
group by 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
SQL # 2
sel
D1.COL_1_CD, D1.COL_1_DESC,
D2.COL_2_CD, D2.COL_2_DESC, D3.COL_3_CD, D3.COL_3_DESC, D4.COL_4_CD,
D4.COL_4_DESC, D5.COL_5_CD, D5.COL_5_DESC,
d1.COL_1_CD_SYS_ID,
d2.COL_2_CD_SYS_ID,
d3.COL_3_CD_SYS_ID,
d4.COL_4_CD_SYS_ID,
d5.COL_5_CD_SYS_ID
from
D1,
D2, D3, D4,
D5
where
D1.COL_1_CD1=D2.COL_2_CD1
and
D2.COL_2_CD1=D3.COL_3_CD1
and
D4.COL_4_CD1=D5.COL_5_CD1
and
D1.COL_1_CD in ('707')
UNION ALL
sel
D1.COL_1_CD, D1.COL_1_DESC,
D2.COL_2_CD, D2.COL_2_DESC, D3.COL_3_CD, D3.COL_3_DESC, D4.COL_4_CD,
D4.COL_4_DESC, D5.COL_5_CD, D5.COL_5_DESC,
d1.COL_1_CD_SYS_ID,
d2.COL_2_CD_SYS_ID,
d3.COL_3_CD_SYS_ID,
d4.COL_4_CD_SYS_ID,
d5.COL_5_CD_SYS_ID
from
D1,
D2, D3, D4,
D5
where
D1.COL_1_CD1=D2.COL_2_CD1
and
D2.COL_2_CD1=D3.COL_3_CD1
and
D4.COL_4_CD1=D5.COL_5_CD1
and
D2.COL_2_CD in ('707')
UNION ALL
.....<same query>
D3.COL_3_CD in ('707')
UNION ALL
.....<same query>
D4.COL_4_CD in ('707')
UNION ALL
.....<same query>
D5.COL_5_CD in ('707')
row counts are not same . What kind of OR logic can be converted as equivalent UNION ALL.
I have a TABLE "elements" with one COLUMN "number", type SMALLINT that contains numbers 1 thru 56. How can I generate unique sets of 5 numbers of every possible combination from 1 to 56, using an SQL statement?
In APL (programming language) a simple dyadic function 5!56 does the trick!
EDIT: In good ole MS-DOS QBASIC, I accomplished it like this:
10 OPEN "C:\5NUMBERS.OUT" FOR OUTPUT ACCESS READ WRITE AS #1
12 LET SER = 0
15 LET E = 56
30 FOR B5 = 5 TO E
40 FOR B4 = 4 TO E
50 FOR B3 = 3 TO E
60 FOR B2 = 2 TO E
70 FOR B1 = 1 TO E
80
88 IF B5 = B1 THEN 190
89 IF B5 = B2 THEN 190
90 IF B5 = B3 THEN 190
91 IF B5 = B4 THEN 190
92 IF B4 = B1 THEN 180
93 IF B4 = B2 THEN 180
94 IF B4 = B3 THEN 180
95 IF B3 = B1 THEN 170
96 IF B3 = B2 THEN 170
97 IF B2 = B1 THEN 160
98 LET SER = SER + 1
100 PRINT #1, SER; "|";
130 PRINT #1, B1; "|";
131 PRINT #1, B2; "|";
132 PRINT #1, B3; "|";
133 PRINT #1, B4; "|";
134 PRINT #1, B5; "|";
140 PRINT #1, B1 + B2 + B3 + B4 + B5; "|"
150 NEXT B1
160 NEXT B2
170 NEXT B3
180 NEXT B4
190 NEXT B5
205 CLOSE
210 END
220 SYSTEM
This, by the way, created my load file into an INFORMIX-SQL table
TABLE combos
(
seq_id SERIAL,
ball_1 SMALLINT,
ball_2 SMALLINT,
ball_3 SMALLINT,
ball_4 SMALLINT,
ball_5 SMALLINT,
sum SMALLINT
);
I used combos.sum to generate a bell curve graph, showing the count of combinations having the same sum of each element.
If by "unique sets" you mean what I think you do (sorry, I don't know APL!), you can write:
SELECT e1.number, e2.number, e3.number, e4.number, e.number
FROM elements e1, elements e2, elements e3, elements e4, elements e5
WHERE e1.number < e2.number
AND e2.number < e3.number
AND e3.number < e4.number
AND e4.number < e5.number
;
"could this be accomplished without actually having to store the
elements in a table?.. i.e. let the server do it without resorting to
table I/O? "
Yes, there is an Oracle trick to generate data on the fly, using the hierarchical query and the CTE syntax:
WITH elements AS
( select rownum as number
from dual
connect by level <= 56 )
SELECT e1.number, e2.number, e3.number, e4.number, e.number
FROM elements e1, elements e2, elements e3, elements e4, elements e5
WHERE e1.number < e2.number
AND e2.number < e3.number
AND e3.number < e4.number
AND e4.number < e5.number
;
If you want to include pairs of identical numbers, e.g. (5,5):
SELECT e1.number AS number1
,e2.number AS number2
FROM elements e1
,elements e2
WHERE e1.number <= e2.number;
If you want to only have different numbers in each pair:
SELECT e1.number AS number1
,e2.number AS number2
FROM elements e1
,elements e2
WHERE e1.number < e2.number;
Not that I would actually use a database for this type of task but, if you were forced to do this under threat of torture or dismemberment, I would look into something like (number shortened to num for formatting purposes):
select a.num, b.num, c.num, d.num, e.num
from elements a, elements b, elements c, elements d, elements e
where a.num <> b.num and a.num <> c.num and a.num <> d.num and a.num <> e.num
and b.num <> c.num and b.num <> d.num and b.num <> e.num
and c.num <> d.num and c.num <> e.num
and d.num <> e.num
It basically cross joins the table to itself to generate five columns and then filters out those where any of the numbers are identical.
Note that this gives you permutations: (1,2,3,4,5) is distinct from (1,2,3,5,4). If you want combinations (where the order doesn't matter), you would use slightly different clauses:
select a.num, b.num, c.num, d.num, e.num
from elements a, elements b, elements c, elements d, elements e
where a.num > b.num and b.num > c.num and c.num > d.num and d.num > e.num
My FIRST thought would be to do a Cartesian and just make sure that every record is higher than the last so you don't ever get numbers duplicated anywhere. Now this would create something like
1,2,3,4,5
1,2,3,4,6
1,2,3,4,7, etc...
but will NEVER have the reverse or mixed such as
6,4,3,2,1
6,2,4,3,1
4,6,1,2,3
as those would already be a "same" set of numbers (more along the lines of lottery style where no same number appears twice)
HOWEVER, if you also wanted duplicates, such as
1,1,1,1,1
1,2,1,2,1
1,2,3,1,1
Where a number COULD get repeated numbers just change the equality to <= instead of just <.
select
YT1.Number as Num1,
YT2.Number as Num2,
YT3.Number as Num3,
YT4.Number as Num4,
YT5.Number as Num5
from
YourTable YT1
JOIN YourTable YT2
ON YT1.Number < YT2.Number
JOIN YourTable YT3
ON YT2.Number < YT3.Number
JOIN YourTable YT4
ON YT3.Number < YT4.Number
JOIN YourTable YT5
ON YT4.Number < YT5.Number