Advent Of Code 2020 in SQL – Day 2: “Password Philosophy”
Tags: adventofcode, sql
Published: 2020-12-08 11:22:14

---
title: Advent Of Code 2020 in SQL – Day 2: “Password Philosophy”
published: true
date: 2020-12-02 08:28:26 UTC
tags: AdventOfCode,SQL
canonical_url: https://cleandatabase.wordpress.com/2020/12/02/advent-of-code-day-2-password-philosophy/
series: Advent Of Code 2020 in SQL
---

[Today’s challenge](https://adventofcode.com/2020/day/2) is to analyze a given list of passwords towards the provided policy.

The policy of part 1 is that the given character must occur in the password inside a given threshold – sounds like we could use some regex.

The biggest challenge – again – was to get the input data into the database (I want to avoid setting up tables if I can). `sys.odcivarchar2list` seems like a good choice, but the input has 1000 entries and the constructor of odcivarchar2list only supports 999 items.

Luckily, we got the `UNION ALL` operator 🙂

```sql
select column_value line from table(sys.odcivarchar2list(
      '9-10 m: mmmmnxmmmwm',
      '6-8 w: wpwwhxnv',
      ... -- up to 999 items
      ))
    union all
    select column_value from table(sys.odcivarchar2list('17-18 b: jnlntbblbbqbkqmbbb'))
```

To make the implementation more readable, I use a lot of WITH-selects. Thanks to `regexp_substr `for parsing and `regexp_count `it’s straight forward to solve the puzzle.

```sql
with
  input as (
    select column_value line 
    from table(sys.odcivarchar2list(...))
  ),
  regex_pattern as (
    select '([0-9]+)-([0-9]+) ([a-z]): ([a-z]+)' pattern 
      from dual
  ),
  parsed_input as (
    select
      regexp_replace(line, regex.pattern, '\1') min_occurence,
      regexp_replace(line, regex.pattern, '\2') max_occurence,
      regexp_replace(line, regex.pattern, '\3') search_char,
      regexp_replace(line, regex.pattern, '\4') password,
      line original_line
      from input, regex_pattern regex
  )
select
  count(*)
  from parsed_input
  where regexp_count(password, search_char) 
    between min_occurence and max_occurence
;
```

Part 2 now changes the policy from analyzing the occurence to check whether exactly one of the chars at the two given positions in the string match the searched character.

Good thing we have the `case `statement in SQL:

```sql
with
  input as (
    select column_value line 
    from table(sys.odcivarchar2list(...))
  ),
  regex_pattern as (
    select '([0-9]+)-([0-9]+) ([a-z]): ([a-z]+)' pattern 
      from dual
  ),
  parsed_input as (
    select
      regexp_replace(line, regex.pattern, '\1') first_position,
      regexp_replace(line, regex.pattern, '\2') second_position,
      regexp_replace(line, regex.pattern, '\3') search_char,
      regexp_replace(line, regex.pattern, '\4') password,
      line original_line
      from input, regex_pattern regex
  ),
  positions_analyzed as (
    select
      password,
      case
        when substr(password, first_position, 1) = search_char 
        then
          1
        else
          0
        end first_pos_found,
      case
        when substr(password, second_position, 1) = search_char 
        then
          1
        else
          0
        end second_pos_found
      from parsed_input
  )
select
  count(*)
  from positions_analyzed
  where (first_pos_found+second_pos_found) = 1;
```

You can find the solution to both parts on [Oracle LiveSQL](https://livesql.oracle.com/apex/livesql/s/k01ipqdmaldiwy08jngaf4ulk).