Struggle to understand SQL query - sql

I'm struggling to understand what the below 2 chunks of code do. Some comments would be really appreciated.
Chunk 1:
SELECT
l
FROM
(
SELECT
level l
FROM
dual
CONNECT BY
level <= 1000
),
(
SELECT
level m
FROM
dual
CONNECT BY
level <= 1000
)
Chunk 2:
HAVING
COUNT(
CASE l / m
WHEN trunc(l / m) THEN
'Y'
END
) = 2
PD: I will post below the entire SQL query to provide more background. The query just gets the prime numbers 1 to 1000 and listaggs them with ampersands.
SELECT
LISTAGG(l, '&') WITHIN GROUP(
ORDER BY
l
)
FROM
(
SELECT
l
FROM
(
SELECT
level l
FROM
dual
CONNECT BY
level <= 1000
),
(
SELECT
level m
FROM
dual
CONNECT BY
level <= 1000
)
WHERE
m <= l
GROUP BY
l
HAVING
COUNT(
CASE l / m
WHEN trunc(l / m) THEN
'Y'
END
) = 2
ORDER BY
l
);

[TL;DR] It is a prime number generator.
SELECT level l FROM dual CONNECT BY level <= 1000
Generates 1000 rows containing incrementally increasing numbers from 1 to 1000 and aliases the column to l.
SELECT level m FROM dual CONNECT BY level <= 1000
Does exactly the same thing but aliases the column to m.
SELECT l
FROM ( SELECT level l FROM dual CONNECT BY level <= 1000 ),
( SELECT level m FROM dual CONNECT BY level <= 1000 )
Applies a CROSS JOIN (using the legacy comma-join syntax) between the two tables and returns only the l column; so you will have 1000 copies of each of the numbers from 1 to 1000 (for a total of 1000000 rows)... or would do if there wasn't the filter condition:
WHERE m <= l
Which filters those rows so you don't get 1000 rows of each number but you will get 1 row with the l value of 1, 2 rows with the l value of 2 (with m values of 1 and 2), 3 rows with the value of 3 (with m values of 1, 2, and 3), ... etc. up to 1000 rows with the value of 1000 (with m values of 1, 2, 3, ..., 999, 1000).
GROUP BY l
HAVING COUNT( CASE l / m WHEN trunc(l / m) THEN 'Y' END ) = 2
Counts the number of factors of l (where the value m divides exactly into l) and filters to only have those rows where there are exactly two factors. Those factors must be the values when m equals 1 and l; which means that there are no other factors of l between the values of 1 and l and so l is a prime number.
ORDER BY l
Then orders the values in ascending order.
A more efficient generator would be:
SELECT 2 AS l FROM DUAL UNION ALL
SELECT 3 AS l FROM DUAL UNION ALL
SELECT l
FROM ( SELECT 2 * level + 3 l FROM dual CONNECT BY 2 * level + 3 <= 1000 )
CROSS APPLY
( SELECT 2 * level + 1 m FROM dual CONNECT BY POWER(2 * level + 1, 2) <= l )
GROUP BY l
HAVING COUNT( CASE l / m WHEN trunc(l / m) THEN 1 END ) = 0
ORDER BY l
Which correlates the row generators to only generate odd numbers and so that the second generator stops generating at the square root of the value of the first generator.

Related

Oracle SQL display distinct none "standard alphanumeric caracters

I need to find the way to list all the characters used in the column in order to narrow down the "Approved" values within the insert template we are creating...
the idea is to allow all letters (only standard) without any dialect / country specific ones.
trying something like this... but need to have a list of the characters left over... like "$%()* etc.
SELECT * FROM mytable WHERE REGEXP_LIKE(column_1,^[a-zA-Z0-9-]+$)
To find the other characters, you could remove the ones you do expect and then see what is left:
SELECT REGEXP_REPLACE( column1, '[a-zA-Z0-9-]' ) AS other_characters
FROM mytable
WHERE REGEXP_REPLACE( column1, '[a-zA-Z0-9-]' ) IS NOT NULL
If you want to concatenate and remove duplicate characters:
WITH replace_expected ( str ) AS (
SELECT REGEXP_REPLACE( column1, '[a-zA-Z0-9-]' )
FROM mytable
WHERE REGEXP_REPLACE( column1, '[a-zA-Z0-9-]' ) IS NOT NULL
),
split_strings ( str, pos, ch ) AS (
SELECT str, 1, SUBSTR(str, 1, 1)
FROM replace_expected
UNION ALL
SELECT str, pos + 1, SUBSTR(str, pos + 1, 1)
FROM split_strings
WHERE pos < LENGTH(str)
)
SELECT LISTAGG(DISTINCT ch) WITHIN GROUP (ORDER BY ch) AS other_characters
FROM split_strings;
fiddle
Two steps:
Extract the special characters from all strings in the column and concatenate them into one long string (with possibly many characters appearing multifold in the string).
Use a recursive query to loop through the characters and return a string with distinct characters.
The query:
with one_row (str) as
(
select listagg(regexp_replace(column_1, '[a-zA-Z0-9\-]'))
from mytable
where regexp_like(column_1, '[^a-zA-Z0-9\-]')
)
select listagg(distinct substr(str, level, 1)) as c
from one_row
connect by level <= length(str);
create table dummy_data as
select rownum as id, dbms_random.string(opt, len) str
from (
select case round(dbms_random.value(0, 3))
when 0 then 'a'
when 1 then 'x'
else 'p'
end opt,
round(dbms_random.value(5, 60)) len
from dual connect by level <= 30 );
SELECT * FROM DUMMY_DATA;
ID STR
1 UMUUJ0R5VM1T3X10TDCNIWC3MQ5ELOB041YMNEJSLT
2 _t8 }LeZhjiMB"8/a'/~a
3 BLSE6XX6SL3M7W0DG3HH28SCHPSAT11ZH2E5DOSKEV3KW9
4 1]Mh58(l<Wa}{
5 :_QiWUkwp}V$}O
6 NC911A4SRN35CNXT2EU5H2GZ67IQQLKH
7 e"8,z$=Yvy5egvEH2KUkNoVjkitd9IMm0ZktsB i(bk4uU]c3;E
8 MgbpIsLZpWEcAghOUKOISA
9 7H02ASKO3CZRN4D5FUNPEU6YUZD
10 KbJ+QrI\l.th%>^f!Io%wshsVA%
11 PO9A47VU7AXI17XYD5VMSWW8E
12 1ILWL4V
13 FgubwibYBytNvmJHxUfG
14 ?[ngH?0!k.onN>mF(nrkO
15 86G0HP3
16 WXDBV3OBMVSDKQ59YT73G0II3U94
17 GP375CFIQPPN6216I5A7L54O
18 i\L<K,"d'ye 6s~_MB0O1 aC$q;T"EaqpZ^s\gIiYu&:%OnhVj]<a]CmOgqM
19 WxUEtr\II(97i7PQ-Z]yqd#&`#CQB0M"c0;{.by9qo#HT
20 IF5OP7KS9AXW91
21 HNcKwxXozXjTVwKeFZDLdmNOzFKKKq
22 4D8CINXIVT244RDDRZ5TSDQ4CRF4
23 3)oxevW-(~=+#cP[^g)##|1.TL-_N9O-Zdgj"cwJC'*NR; FtK)K
24 AndzeLIEPklDuTWWEBrKrdNKdXwMGeLauJkRzKpKHEGAsxlEXliwBTHdK
25 dlEX1tGFuU5\5+{5`R
26 /W0.{B&)ax&lWEE#OSw
27 CBKOVLKDFKC3EVR
28 :V#Lc.Z"8[O-)cAWUpMjc?j\Kj?xV#%`Yp [VkEV1
29 P9P047
30 W)S<fB`F;N_brMP
with
h (ok_chars) as (
select '0123456789' || 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' ||
'abcdefghijklmnopqrstuvwxyz'
from dual
)
select c, count(*) as cnt
from dummy_data cross join lateral (
select substr(str, level, 1) as c
from h
where instr(ok_chars, substr(str, level, 1)) = 0
connect by level <= length(str)
)
group by c;
C CNT
} 4
- 5
' 3
< 4
, 2
= 2
_ 5
( 5
^ 3
[ 4
" 7
/ 3
: 3
. 6
% 5
! 2
? 4
` 4
8
; 5
+ 3
> 2
) 6
| 1
* 1
~ 3
\ 6
& 4
# 5
] 5
{ 4
# 4
$ 3

Connect by in cross apply

I have the below code:
SELECT
m, l
FROM
(
SELECT
2 * level + 3 l
FROM
dual
CONNECT BY
2 * level + 3 <= 1000
) CROSS APPLY (
SELECT
2 * level + 1 m
FROM
dual
CONNECT BY
power(2 * level + 1, 2) <= l
) order by m, l
However, the first results shown are the below. Why is the 2nd power of m connecting with l in that case if power(3, 2) is not <= 5 (or 7)?
m l
3 5
3 7
Because CONNECT BY checks whether the query will recurse after the first row is generated so you will always get at least one row (where LEVEL is 1); if you want to limit the number of rows then you need a START WITH or WHERE clause.
You can see the difference if you also include either clause and OUTER APPLY to return the l rows when m then has no generated rows (and would be NULL):
SELECT m, l
FROM ( SELECT 2 * level + 3 AS l
FROM dual
CONNECT BY 2 * level + 3 <= 25 )
OUTER APPLY
( SELECT 2 * level + 1 AS m
FROM dual
START WITH 9 <= l
CONNECT BY power(2 * level + 1, 2) <= l )
order by l, m
or
SELECT m, l
FROM ( SELECT 2 * level + 3 AS l
FROM dual
CONNECT BY 2 * level + 3 <= 25 )
OUTER APPLY
( SELECT 2 * level + 1 AS m
FROM dual
WHERE power(2 * level + 1, 2) <= l
CONNECT BY power(2 * level + 1, 2) <= l )
order by l, m
or
Outputs:
M
L
5
7
3
9
3
11
3
13
3
15
3
17
3
19
3
21
3
23
3
25
5
25
db<>fiddle here

flatten list of ranges to single result range set

I am trying to "flatten" a list of ranges in a defined order (alphabetically by name in the examples provided) to a single merged result. Newer Ranges overwrite values of older ranges. Conceptually it looks like this, with "e" being the newest range:
0 1 2 3 4 5 6 7
|-------------a-------------|
|---b---|
|---c---|
|---d---|
|---e---|
|-a-|---c---|---e---|-d-|-a-| <-- expected result
To prevent further confusion: The expected result here is indeed correct. The values 0 - 7 are just the ranges' values, not a progression in time. I use integers for simplicity here, but the values might not be discrete but continuous.
Note that b is completely overshadowed and not relevant anymore.
the data may be modeled like this in SQL:
create table ranges (
name varchar(1),
range_start integer,
range_end integer
);
insert into ranges (name, range_start, range_end) values ('a', 0, 7);
insert into ranges (name, range_start, range_end) values ('b', 2, 4);
insert into ranges (name, range_start, range_end) values ('c', 1, 3);
insert into ranges (name, range_start, range_end) values ('d', 4, 6);
insert into ranges (name, range_start, range_end) values ('e', 3, 5);
-- assume alphabetical order by name
It would be perfect if there was a way to directly query the result in SQL, e.g. like this:
select *magic* from ranges;
-- result:
+------+-------------+-----------+
| a | 0 | 1 |
| c | 1 | 3 |
| e | 3 | 5 |
| d | 5 | 6 |
| a | 6 | 7 |
+------+-------------+-----------+
But I suspect that is not realistically feasible, therefore I need to at least filter out all ranges that are overshadowed by newer ones, as is the case for b in the example above. Otherwise the query would need to transfer more and more irrelevant data as the database grows and new ranges overshadow older ones. For the example above, such a query could return all entries except for b, e.g.:
select *magic* from ranges;
-- result:
+------+-------------+-----------+
| a | 0 | 7 |
| c | 1 | 3 |
| d | 4 | 6 |
| e | 3 | 5 |
+------+-------------+-----------+
I was unable to construct such a filter in SQL. The only thing I managed to do is query all data and then calculate the result in code, for example in Java using the Google Guava library:
final RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closedOpen(0, 7), "a");
rangeMap.put(Range.closedOpen(2, 4), "b");
rangeMap.put(Range.closedOpen(1, 3), "c");
rangeMap.put(Range.closedOpen(4, 6), "d");
rangeMap.put(Range.closedOpen(3, 5), "e");
System.out.println(rangeMap);
// result: [[0..1)=a, [1..3)=c, [3..5)=e, [5..6)=d, [6..7)=a]
Or by hand in python:
import re
from collections import namedtuple
from typing import Optional, List
Range = namedtuple("Range", ["name", "start", "end"])
def overlap(lhs: Range, rhs: Range) -> Optional[Range]:
if lhs.end <= rhs.start or rhs.end <= lhs.start:
return None
return Range(None, min(lhs.start, rhs.start), max(lhs.end, rhs.end))
def range_from_str(str_repr: str) -> Range:
name = re.search(r"[a-z]+", str_repr).group(0)
start = str_repr.index("|") // 4
end = str_repr.rindex("|") // 4
return Range(name, start, end)
if __name__ == '__main__':
ranges: List[Range] = [
# 0 1 2 3 4 5 6 7
range_from_str("|-------------a-------------|"),
range_from_str(" |---b---| "),
range_from_str(" |---c---| "),
range_from_str(" |---d---| "),
range_from_str(" |---e---| "),
# result: |-a-|---c---|---e---|-d-|-a-|
]
result: List[Range] = []
for range in ranges:
for i, res in enumerate(result[:]):
o = overlap(range, res)
if o:
result.append(Range(res.name, o.start, range.start))
result.append(Range(res.name, range.end, o.end))
result[i] = Range(res.name, 0, 0)
result.append(range)
result = sorted(filter(lambda r: r.start < r.end, result), key=lambda r: r.start)
print(result)
# result: [Range(name='a', start=0, end=1), Range(name='c', start=1, end=3), Range(name='e', start=3, end=5), Range(name='d', start=5, end=6), Range(name='a', start=6, end=7)]
The following simple query returns all smallest intervals with top name:
with
all_points(x) as (
select range_start from ranges
union
select range_end from ranges
)
,all_ranges(range_start, range_end) as (
select *
from (select
x as range_start,
lead(x) over(order by x) as range_end
from all_points)
where range_end is not null
)
select *
from all_ranges ar
cross apply (
select max(name) as range_name
from ranges r
where r.range_end >= ar.range_end
and r.range_start <= ar.range_start
)
order by 1,2;
Results:
RANGE_START RANGE_END RANGE_NAME
----------- ---------- ----------
0 1 a
1 2 c
2 3 c
3 4 e
4 5 e
5 6 d
6 7 a
So we need to merge connected intervals with the same names:
Final query without new oracle-specific features
with
all_points(x) as (
select range_start from ranges
union
select range_end from ranges
)
,all_ranges(range_start, range_end) as (
select *
from (select
x as range_start,
lead(x) over(order by x) as range_end
from all_points)
where range_end is not null
)
select
grp,range_name,min(range_start) as range_start,max(range_end) as range_end
from (
select
sum(start_grp_flag) over(order by range_start) grp
,range_start,range_end,range_name
from (
select
range_start,range_end,range_name,
case when range_name = lag(range_name)over(order by range_start) then 0 else 1 end start_grp_flag
from all_ranges ar
cross apply (
select max(name) as range_name
from ranges r
where r.range_end >= ar.range_end
and r.range_start <= ar.range_start
)
)
)
group by grp,range_name
order by 1;
Results:
GRP RANGE_NAME RANGE_START RANGE_END
---------- ---------- ----------- ----------
1 a 0 1
2 c 1 3
3 e 3 5
4 d 5 6
5 a 6 7
Or using actual oracle specific features:
with
all_ranges(range_start, range_end) as (
select * from (
select
x as range_start,
lead(x) over(order by x) as range_end
from (
select distinct x
from ranges
unpivot (x for r in (range_start,range_end))
))
where range_end is not null
)
select *
from all_ranges ar
cross apply (
select max(name) as range_name
from ranges r
where r.range_end >= ar.range_end
and r.range_start <= ar.range_start
)
match_recognize(
order by range_start
measures
first(range_start) as r_start,
last(range_end) as r_end,
last(range_name) as r_name
pattern(STRT A*)
define
A as prev(range_name)=range_name and prev(range_end) = range_start
);
Here is a hierarchical query that would give you the desired output:
WITH ranges(NAME, range_start, range_end) AS
(SELECT 'a', 0, 7 FROM dual UNION ALL
SELECT 'b', 2, 4 FROM dual UNION ALL
SELECT 'c', 1, 3 FROM dual UNION ALL
SELECT 'd', 4, 6 FROM dual UNION ALL
SELECT 'e', 3, 5 FROM dual UNION ALL
SELECT 'f', -3, -2 FROM dual UNION ALL
SELECT 'g', 8, 20 FROM dual UNION ALL
SELECT 'h', 12, 14 FROM dual)
, rm (NAME, range_start, range_end) AS
(SELECT r.*
FROM (SELECT r.NAME
, r.range_start
, NVL(r2.range_start, r.range_end) range_end
FROM ranges r
OUTER apply (SELECT *
FROM ranges
WHERE range_start BETWEEN r.range_start AND r.range_end
AND NAME > r.NAME
ORDER BY range_start, NAME DESC
FETCH FIRST 1 ROWS ONLY) r2
ORDER BY r.range_start, r.NAME desc
FETCH FIRST 1 ROWS ONLY) r
UNION ALL
SELECT r2.NAME
, r2.range_start
, r2.range_end
FROM rm
CROSS apply (SELECT r.NAME
, GREATEST(rm.range_end, r.range_start) range_start
, NVL(r2.range_start, r.range_end) range_end
FROM ranges r
OUTER apply (SELECT *
FROM ranges
WHERE range_start BETWEEN GREATEST(rm.range_end, r.range_start) AND r.range_end
AND NAME > r.NAME
ORDER BY range_start, NAME DESC
FETCH FIRST 1 ROWS ONLY) r2
WHERE r.range_end > rm.range_end
AND NOT EXISTS (SELECT 1 FROM ranges r3
WHERE r3.range_end > rm.range_end
AND (GREATEST(rm.range_end, r3.range_start) < GREATEST(rm.range_end, r.range_start)
OR (GREATEST(rm.range_end, r3.range_start) = GREATEST(rm.range_end, r.range_start)
AND r3.NAME > r.NAME)))
FETCH FIRST 1 ROWS ONLY) r2)
CYCLE NAME, range_start, range_end SET cycle TO 1 DEFAULT 0
SELECT * FROM rm
First you get the first entry ordered by range_start desc, name which will give you the most resent entry with the lowest name.
Then you search for a range with higher name that intersect with this range. If there is one the range_start of this interval will be the range_end of you final interval.
With this start you search more or less the next entry with the same condition.
There is also another less effective but easier and shorter approach: to generate all points and just aggregate them.
For example this simple query will generate all intermediate points:
select x,max(name)
from ranges,
xmltable('xs:integer($A) to xs:integer($B)'
passing range_start as a
,range_end as b
columns x int path '.'
)
group by x
Results:
X M
---------- -
0 a
1 c
2 c
3 e
4 e
5 e
6 d
7 a
Then we can merge them:
select *
from (
select x,max(name) name
from ranges,
xmltable('xs:integer($A) to xs:integer($B)-1'
passing range_start as a
,range_end as b
columns x int path '.'
)
group by x
order by 1
)
match_recognize(
order by x
measures
first(x) as r_start,
last(x)+1 as r_end,
last(name) as r_name
pattern(STRT A*)
define
A as prev(name)=name and prev(x)+1 = x
);
Results:
R_START R_END R
---------- ---------- -
0 1 a
1 3 c
3 5 e
5 6 d
6 7 a
I don't understand your results -- as I've explained in the comments. The "b" should be present, because it is most recent at time 2.
That said, the idea is to unpivot the times and figure out the most recent name at each time -- both beginnings and ends. Then, combine these using gaps-and-islands ideas. This is what the query looks like:
with r as (
select name, range_start as t
from ranges
union all
select null, range_end as t
from ranges
),
r2 as (
select r.*,
(select r2.name
from ranges r2
where r2.range_start <= r.t and
r2.range_end >= r.t
order by r2.range_start desc
fetch first 1 row only
) as imputed_name
from (select distinct t
from r
) r
)
select imputed_name, t,
lead(t) over (order by t)
from (select r2.*,
lag(imputed_name) over ( order by t) as prev_imputed_name
from r2
) r2
where prev_imputed_name is null or prev_imputed_name <> imputed_name;
Here is a db<>fiddle.
Basically the same code should run in Postgres as well.

Oracle - theoretical sql query for create intervals

Is it possible to solve this situation by sql query in ORACLE?
I have a table like this:
TYPE UNIT
A 230
B 225
C 60
D 45
E 5
F 2
I need to separate units to the three(variable) 'same'(equally sized) intervals and foreach figure out the count? It means something like this:
0 - 77 -> 4
78 - 154 -> 0
155 - 230 -> 2
You can use the maximum value and a connect-by query to generate the upper and lower values for each range:
select ceil((level - 1) * int) as int_from,
floor(level * int) - 1 as int_to
from (select round(max(unit) / 3) as int from t42)
connect by level <= 3;
INT_FROM INT_TO
---------- ----------
0 76
77 153
154 230
And then do a left outer join to your original table to do the count for each range, so you get the zero value for the middle range:
with intervals as (
select ceil((level - 1) * int) as int_from,
floor(level * int) - 1 as int_to
from (select round(max(unit) / 3) as int from t42)
connect by level <= 3
)
select i.int_from || '-' || i.int_to as range,
count(t.unit)
from intervals i
left join t42 t
on t.unit between i.int_from and i.int_to
group by i.int_from, i.int_to
order by i.int_from;
RANGE COUNT(T.UNIT)
---------- -------------
0-76 4
77-153 0
154-230 2
Yes, this can be done in Oracle. The hard part is the definition of the bounds. You can use the maximum value and some arithmetic on a sequence with values of 1, 2, and 3.
After that, the rest is just a cross join and aggregation:
with bounds as (
select (case when n = 1 then 0
when n = 2 then trunc(maxu / 3)
else trunc(2 * maxu / 3)
end) as lowerbound,
(case when n = 1 then trunc(maxu / 3)
when n = 2 then trunc(2*maxu / 3)
else maxu
end) as upperbound
from (select 1 as n from dual union all select 2 from dual union all select 3 from dual
) n cross join
(select max(unit) as maxu from atable t)
)
select b.lowerbound || '-' || b.upperbound,
sum(case when units between b.lowerbound and b.upperbound then 1 else 0 end)
from atable t cross join
bounds b
group by b.lowerbound || '-' || b.upperbound;

Oracle Regular Expression To replace A with Z, B with Y, C with X and So on

I want a regular Expression which replaces a string with its respective character in descending order including number:
i.e.
replace a alphanumeric string with descending order string
if special characters are found in sting keep the as it is
I means :
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Z Y X W V U T S R Q P O N M L K J I H G F E D C B A
0 1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1 0
Few examples:
1) ABC*1230 -> ZYX*8739
2) Hello World! -> Svool Dliow!
3) Good & Bad -> Tllw & Yzw
I'd use the Oracle TRANSLATE function for this:
TRANSLATE(myValue,
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
'ZYXWVUTSRQPONMLKJIHGFEDCBA9876543210')
Here it is done with REGEXP:
SELECT LISTAGG(DECODE(o_str_level,NULL,srch_l.rs,o_str_level)) WITHIN GROUP (ORDER BY srch_l.ri) AS the_result
FROM
(
SELECT REGEXP_SUBSTR(t_str, '.', 1, LEVEL) rs
, REGEXP_INSTR (t_str, '.', 1, LEVEL) ri
FROM
(
SELECT 'ABCDEF TUVWXYZ' AS t_str FROM DUAL
) test_str
CONNECT BY LEVEL <= LENGTH(t_str)
) srch_l
LEFT JOIN
(
SELECT REGEXP_SUBSTR(r_str, '\w', 1, LEVEL) AS r_str_level
, REGEXP_SUBSTR(o_str, '\w', 1, LEVEL) AS o_str_level
FROM
(
SELECT 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' r_str, 'ZYXWVUTSRQPONMLKJIHGFEDCBA' o_str FROM DUAL
)
CONNECT BY LEVEL <= 26
) apt
ON srch_l.rs = apt.r_str_level;
-- Result:
-- ZYXWVU GFEDCBA
Is it simpler than TRANSLATE? Almost...