<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: geraldew</title>
    <description>The latest articles on Forem by geraldew (@geraldew).</description>
    <link>https://forem.com/geraldew</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F159220%2F87442881-9946-4a0c-997a-d63c76f2dcf6.png</url>
      <title>Forem: geraldew</title>
      <link>https://forem.com/geraldew</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/geraldew"/>
    <language>en</language>
    <item>
      <title>Some Example SQL</title>
      <dc:creator>geraldew</dc:creator>
      <pubDate>Fri, 18 Jul 2025 16:02:36 +0000</pubDate>
      <link>https://forem.com/geraldew/some-example-sql-12g3</link>
      <guid>https://forem.com/geraldew/some-example-sql-12g3</guid>
      <description>&lt;h2&gt;
  
  
  Prologue
&lt;/h2&gt;

&lt;p&gt;I thought it might be interesting to share an example of the sort of code I write in my day job.&lt;/p&gt;

&lt;p&gt;Much of the time, what I write is so inherently arcane by being specific to my work environment, that it would be way too much trouble to anonymise it for sharing in public.&lt;/p&gt;

&lt;p&gt;However I recently wrote something that felt quite shareable &lt;/p&gt;

&lt;p&gt;I have translated the idiom of the code into something that feels like it would have a similar-enough mathematical pattern.&lt;/p&gt;

&lt;p&gt;By chance, outside of my work I hade been reading some description of the dynamics of authors and publishers, and that struck me as almost suitable. Frankly, it doesn't quite work, but it will do until I find another paradigm.&lt;/p&gt;

&lt;p&gt;Ideally I would like to find some publicly available example data that fits here. Suggestions are welcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Base Data
&lt;/h2&gt;

&lt;p&gt;So before we get to the code I wrote, here is our example base data structure. I've imagined a dataset listing per-year connections between authors and publishers, where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;each author has only one publisher for a given year&lt;/li&gt;
&lt;li&gt;publishers can have any number of authors each year&lt;/li&gt;
&lt;li&gt;publishers might just not be active some years - if that sounds strange, just think of it as meaning that from one year to the next, a publisher can begin to operate, or cease to operate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So our base table is just a triple of ID numbers for the author and publisher each, and a year. Each triple is unique. More about this in the commentary after the SQL script.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Base Table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt;
  &lt;span class="n"&gt;Dbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data_AuthorYearPublisher&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;Pblshr_Num&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Acnt_Yr&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Athr_Id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
  &lt;span class="n"&gt;Pblshr_Num&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Athr_Id&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Analytical SQL
&lt;/h2&gt;

&lt;p&gt;Because there's a fair bit of SQL here, I have a choice of two ways to present it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;show each step as an explained piece and then put it all together&lt;/li&gt;
&lt;li&gt;show the whole lot and then explain each part&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I will do is the second method, so I can post the whole lot, and then revisit this page to add explanations as I find time to do do.&lt;/p&gt;

&lt;p&gt;Of course, in reality I wrote it by starting with the base table, writing a treatment of it, then writing a treatment of the treatment and so on, until I had something that gave me the kind of understanding of the data that I'd vaguely had in mind.&lt;/p&gt;

&lt;p&gt;So while I will at this point just dump all the SQL in a code block, I can say that the general structure is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a common table expression (CTE) named "Rollup_Publisher_Year" pulling from the base table&lt;/li&gt;
&lt;li&gt;a CTE "Chunkify" from "Rollup_Publisher_Year"&lt;/li&gt;
&lt;li&gt;a CTE "Rollup_Publisher" from Chunkify&lt;/li&gt;
&lt;li&gt;a CTE "Patterns" from Rollup_Publisher&lt;/li&gt;
&lt;li&gt;a CTE "RollingSums" from Patterns&lt;/li&gt;
&lt;li&gt;a CTE "DoProportionCalcs" from RollingSums&lt;/li&gt;
&lt;li&gt;a final SELECT from DoProportionCalcs that just gives a different sequence for the columns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And generally, apart from being translated into the terminology of authors and publishers the identifier names are unchanged from original code.&lt;/p&gt;

&lt;p&gt;Finally, to give an idea of timeliness to the writing of this SQL, it came from reaching a "next thing to do" late on a Thursday afternoon. I started tackling it at about 5pm and by 7pm had the SELECT of the "Patterns" step running. Overnight I thought of the idea for the "X0", "X1", "X2" encoding and after a morning diversion, spent the next afternoon pushing through to the code you see here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Analyse Over-the-Year Patterns of Author Counts of Publishers&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt;
  &lt;span class="n"&gt;Rollup_Publisher_Year&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;Pblshr_Num&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;YEAR&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Current_Yr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Current_Yr&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;Athr_Id&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;Dbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data_AuthorYearPublisher&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
  &lt;span class="n"&gt;Pblshr_Num&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Current_Yr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;BackByYearsN&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Chunkify&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;Pblshr_Num&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;CASE&lt;/span&gt;
    &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'00'&lt;/span&gt;
    &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'X0'&lt;/span&gt;
    &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'X1'&lt;/span&gt;
    &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'X2'&lt;/span&gt;
    &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'X3'&lt;/span&gt;
    &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'X4'&lt;/span&gt;
    &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'X5+'&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;AthrCnt_Cd&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;Rollup_Publisher_Year&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Rollup_Publisher&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;Pblshr_Num&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="c1"&gt;-- derive a backward sequence string, for now, just to illustrate their cliemt pattern&lt;/span&gt;
    &lt;span class="c1"&gt;--MAX( CASE WHEN BackByYearsN = 0 THEN CAST( Athr_Ctd AS VARCHAR( 10) ) ELSE '' END ) || '-&amp;lt;-' ||&lt;/span&gt;
    &lt;span class="c1"&gt;--MAX( CASE WHEN BackByYearsN = 1 THEN CAST( Athr_Ctd AS VARCHAR( 10) ) ELSE '' END ) || '-&amp;lt;-' ||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;lt;-'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;lt;-'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;lt;-'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;lt;-'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;lt;-'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;lt;-'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;lt;-'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;BackYears_Athr_Ctd_Str&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="c1"&gt;-- derive a backward sequence string, for now, just to illustrate their cliemt pattern&lt;/span&gt;
    &lt;span class="c1"&gt;--MAX( CASE WHEN BackByYearsN = 0 THEN AthrCnt_Cd ELSE '' END ) || '-&amp;lt;-' ||&lt;/span&gt;
    &lt;span class="c1"&gt;--MAX( CASE WHEN BackByYearsN = 1 THEN AthrCnt_Cd ELSE '' END ) || '-&amp;lt;-' ||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;AthrCnt_Cd&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;lt;-'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;AthrCnt_Cd&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;lt;-'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;AthrCnt_Cd&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;lt;-'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;AthrCnt_Cd&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;lt;-'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;AthrCnt_Cd&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;lt;-'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;AthrCnt_Cd&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;lt;-'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;AthrCnt_Cd&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'-&amp;lt;-'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;AthrCnt_Cd&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;BackYears_AthrCntCd_Str&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;--&lt;/span&gt;
  &lt;span class="k"&gt;MIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Min_Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Max_Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;--&lt;/span&gt;
  &lt;span class="k"&gt;MIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Min_BackByYearsN&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Avg_BackByYearsN&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;BackByYearsN&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Max_BackByYearsN&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;--&lt;/span&gt;
  &lt;span class="k"&gt;MIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Min_Athr_Ctd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Avg_Athr_Ctd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Max_Athr_Ctd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;--&lt;/span&gt;
  &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;Acnt_Yr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2024&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Athrs_2024_Ctd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;Acnt_Yr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;Athr_Ctd&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Athrs_2023_Ctd&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="c1"&gt;-- Rollup_Publisher_Year&lt;/span&gt;
  &lt;span class="n"&gt;Chunkify&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
  &lt;span class="n"&gt;Pblshr_Num&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Patterns&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;BackYears_AthrCntCd_Str&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Pblshr_Num&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;--&lt;/span&gt;
  &lt;span class="c1"&gt;-- derive a value that can later be checked for being more than zero - the actual amount doesn't itself have a meaning&lt;/span&gt;
  &lt;span class="c1"&gt;-- the idea is that this value can only be zero if both 2014 and 2023 were zero&lt;/span&gt;
  &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;ZEROIFNULL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athrs_2024_Ctd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ZEROIFNULL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athrs_2023_Ctd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;MaxAthrsCtd_24plus23&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;--&lt;/span&gt;
  &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Max_Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Max_Max_Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Min_Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Max_Min_Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;MIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Min_Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Min_Min_Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;--&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athrs_2024_Ctd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Sum_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Athrs_2023_Ctd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Sum_Athrs_2023_Ctd&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;Rollup_Publisher&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
  &lt;span class="n"&gt;BackYears_AthrCntCd_Str&lt;/span&gt;
&lt;span class="c1"&gt;-- ORDER BY&lt;/span&gt;
&lt;span class="c1"&gt;--  Publisher_Cnt DESC&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;RollingSums&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;MaxAthrsCtd_24plus23&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'24 or 23'&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;RecentlyActive&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;Sum_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Acted_2024'&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Active2024&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;FIRST_VALUE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;    
    &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
      &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Sum_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Sum_Athrs_2023_Ctd&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;IgnoreFirstPattern_Cnt&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;--&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
      &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Sum_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Sum_Athrs_2023_Ctd&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
    &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="n"&gt;UNBOUNDED&lt;/span&gt; &lt;span class="k"&gt;PRECEDING&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SumPblshrsSoFar&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SumPblshrsOfAll&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;--&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;MaxAthrsCtd_24plus23&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
      &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Sum_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Sum_Athrs_2023_Ctd&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
    &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="n"&gt;UNBOUNDED&lt;/span&gt; &lt;span class="k"&gt;PRECEDING&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SumActvPblshrsSoFar&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;MaxAthrsCtd_24plus23&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SumActvPblshrsOfAll&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;--&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;Sum_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
      &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Sum_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Sum_Athrs_2023_Ctd&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
    &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="n"&gt;UNBOUNDED&lt;/span&gt; &lt;span class="k"&gt;PRECEDING&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SumActv24PblshrsSoFar&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;Sum_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SumActv24PblshrsOfAll&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;--&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Sum_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
      &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Sum_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Sum_Athrs_2023_Ctd&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
    &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="n"&gt;UNBOUNDED&lt;/span&gt; &lt;span class="k"&gt;PRECEDING&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SumSoFar_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Sum_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SumofAll_Athrs_2024_Ctd&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;Patterns&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;DoProportionCalcs&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;R_S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;SumActvPblshrsSoFar&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;SumActvPblshrsOfAll&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SoFar_ActvPblshrs_Ppn&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;SumActv24PblshrsSoFar&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;SumActv24PblshrsOfAll&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SoFar_Actv24Pblshrs_Ppn&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;SumPblshrsSoFar&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;SumPblshrsOfAll&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SoFar_Pblshrs_Ppn&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;R_S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SumPblshrsSoFar&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;IgnoreFirstPattern_Cnt&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SoFar_WrkblPblshrs&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;SoFar_WrkblPblshrs&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;SumPblshrsOfAll&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;IgnoreFirstPattern_Cnt&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SoFar_WrkblPblshrs_Ppn&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;--&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;SumSoFar_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;SumofAll_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SoFar_Athrs2024_Ppn&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;RollingSums&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;R_S&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="c1"&gt;-- Grouped By&lt;/span&gt;
  &lt;span class="n"&gt;BackYears_AthrCntCd_Str&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;-- Agggregates from here on&lt;/span&gt;
  &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;-- using the idea of Active only about 2024&lt;/span&gt;
  &lt;span class="n"&gt;Active2024&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SumActv24PblshrsSoFar&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SoFar_Actv24Pblshrs_Ppn&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SumActv24PblshrsOfAll&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;-- and their Authrs&lt;/span&gt;
  &lt;span class="n"&gt;Sum_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SumSoFar_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SoFar_Athrs2024_Ppn&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SumofAll_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;-- some year spread indicators&lt;/span&gt;
  &lt;span class="n"&gt;Max_Max_Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Max_Min_Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Min_Min_Acnt_Yr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;--&lt;/span&gt;
  &lt;span class="c1"&gt;-- All Publishers, no filtering&lt;/span&gt;
  &lt;span class="n"&gt;SumPblshrsSoFar&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SoFar_Pblshrs_Ppn&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SumPblshrsOfAll&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;-- Ignoring the long-time inactive Publishers&lt;/span&gt;
  &lt;span class="n"&gt;IgnoreFirstPattern_Cnt&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SoFar_WrkblPblshrs&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SoFar_WrkblPblshrs_Ppn&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;--&lt;/span&gt;
  &lt;span class="c1"&gt;-- Idea of Active in either 2024 or 2023&lt;/span&gt;
  &lt;span class="n"&gt;MaxAthrsCtd_24plus23&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Sum_Athrs_2023_Ctd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;RecentlyActive&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SumActvPblshrsSoFar&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SoFar_ActvPblshrs_Ppn&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SumActvPblshrsOfAll&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;DoProportionCalcs&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; 
  &lt;span class="n"&gt;Publisher_Cnt&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Sum_Athrs_2024_Ctd&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Sum_Athrs_2023_Ctd&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Commentary
&lt;/h2&gt;

&lt;p&gt;Note that I said this "doesn't quite work" - that's because in real life, authors probably use multiple publishers simultaneously. But here we'll imagine that doesn't happen - though we will allow authors to change publisher from one year to the next.&lt;/p&gt;

&lt;p&gt;That's important for one important aspect of this analysis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we cannot ever add the counts of authors across the years&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  To be continued...
&lt;/h2&gt;

</description>
      <category>sql</category>
    </item>
    <item>
      <title>Just some example work notes</title>
      <dc:creator>geraldew</dc:creator>
      <pubDate>Sun, 22 Jun 2025 15:36:47 +0000</pubDate>
      <link>https://forem.com/geraldew/just-some-example-work-notes-5955</link>
      <guid>https://forem.com/geraldew/just-some-example-work-notes-5955</guid>
      <description>&lt;p&gt;Note for readers at Dev.to - this posting is not intended for general reading as is. However I may later write an article that refers to it. Not that anyone reads my posts here anyway.&lt;/p&gt;

&lt;p&gt;The context for it, is some coding in progress at &lt;a href="https://gitlab.com/geraldew/mexenum" rel="noopener noreferrer"&gt;mexenum&lt;/a&gt; which comes from ongoing work on &lt;a href="https://gitlab.com/geraldew/foldatry" rel="noopener noreferrer"&gt;Foldatry&lt;/a&gt; and its companion application &lt;a href="https://gitlab.com/geraldew/diskartulary" rel="noopener noreferrer"&gt;Diskartulary&lt;/a&gt; - but frankly that information while true would be way too tedious to try to describe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This is some general musing as I seek to build out a general purpose body of code that will handle vital matters of custom values in my applications as well as the ability to save and reload them as "settings".&lt;/p&gt;

&lt;p&gt;See "Stages of implementation" for what has come before this. These notes are being written to clarify ideas during the transition from stage "First abstraction" into "Second abstraction".&lt;/p&gt;

&lt;h2&gt;
  
  
  Layers
&lt;/h2&gt;

&lt;p&gt;We're going to go over this set three times:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;once just to list them&lt;/li&gt;
&lt;li&gt;then with broad descriptions&lt;/li&gt;
&lt;li&gt;then in more detail&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  List
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Meta Extended Enumerations&lt;/li&gt;
&lt;li&gt;Data types of Settings&lt;/li&gt;
&lt;li&gt;A Settings Collection&lt;/li&gt;
&lt;li&gt;Loadable Settings Collection&lt;/li&gt;
&lt;li&gt;Savable Settings Collection&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Descriptions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Meta Extended Enumerations = each Enumeration becomes a matrix of values, with a key and attributes - these will be used for various things throughout the program - really, everywhere that the application needs a definitive finite set of optional values.&lt;/li&gt;
&lt;li&gt;Data types of Settings = because we will restrict this "Settings ability" to a fairly small number of data types - but it will include the ability to be told how to use other enumerations that are custom to each application&lt;/li&gt;
&lt;li&gt;A Settings Collection = because each application will create and use (at least) one of these. This is something that can be used internally by the application. In that context, them being in a collection may or may not be of some use.&lt;/li&gt;
&lt;li&gt;Loadable Settings Collection = which will provide an ability to reliably read settings in from a JSON file, which includes handling their custom enumerations&lt;/li&gt;
&lt;li&gt;Savable Settings Collection = which will provide an ability to reliably save settings out to a JSON file, with an inherent facility for controlling which among all the settings will be thus output&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Details
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Meta Extended Enumerations

&lt;ul&gt;
&lt;li&gt;named-tuple of attributes with a type enforcement function for it&lt;/li&gt;
&lt;li&gt;class of meta-extended-enumeration&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Data types of Settings

&lt;ul&gt;
&lt;li&gt;an enumeration to cover five pairs of types&lt;/li&gt;
&lt;li&gt;a named-tuple for extra attributes, including references for functions to handle custom enumerations&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;A Settings Collection

&lt;ul&gt;
&lt;li&gt;a class to hold a set of the settings&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Loadable Settings Collection

&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Savable Settings Collection&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  As Seen From JSON
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt; which application is it for&lt;/li&gt;
&lt;li&gt; which version of the application made it&lt;/li&gt;
&lt;li&gt; dictionary of keys and values

&lt;ul&gt;
&lt;li&gt; name of key&lt;/li&gt;
&lt;li&gt; string representation of value (might be a single value, or  a list of values)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;When reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;confirm it is for the application&lt;/li&gt;
&lt;li&gt;confirm we know how to handle settings from that version&lt;/li&gt;
&lt;li&gt;loop

&lt;ul&gt;
&lt;li&gt;read a key - confirm we recognise it

&lt;ul&gt;
&lt;li&gt;read the value, if this should be an enumeration value, then convert it&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Therefore, things we need at read time are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a function to check the application code&lt;/li&gt;
&lt;li&gt;a function to check the version code&lt;/li&gt;
&lt;li&gt;a function to look up a key&lt;/li&gt;
&lt;li&gt;a function to handle the value for that key&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stages of implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Initial stage
&lt;/h3&gt;

&lt;p&gt;Implemented "manually" inside Foldatry.&lt;/p&gt;

&lt;p&gt;All the enumerations used around the application are separately declared and used where appropriate.&lt;/p&gt;

&lt;p&gt;For each enumeration an addtional extended-enumeration was manually crafted, which included the generation of a list of tuples for passing to a combobox widget&lt;/p&gt;

&lt;p&gt;The Settings collection was manually implemented as an enumeration that lists all the settings, plus a custom extended-enumeration for each. There is a run-time dictionary keyed on that enumeration, holding the extended-enumerations.&lt;/p&gt;

&lt;p&gt;During loading from JSON file, the keys were manually given direction of which value enumeration to use (to decode from text values). In non-GUi mode, when loading from a file, all keys present in it are applied as settings.&lt;/p&gt;

&lt;p&gt;An additional GuiSettings object then added a boolean flag per setting, allowing control of which gets saved and which gets loaded. Thus there is an ability to save a partial set of settings to a file, thereby allowing for separate files for subsets of settings, which could then be progressively loaded via the non-GUI mode.&lt;/p&gt;

&lt;h4&gt;
  
  
  locations of those in the modules
&lt;/h4&gt;

&lt;p&gt;As for where the code for the above lives, in terms of the Foldatry Python project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the extended-enumerations were all placed in the module "applitry"&lt;/li&gt;
&lt;li&gt;the settings class-and-object and the gui-settings class-and-object were both placed in the main "foldatry" module, which currently handles both the GUI mode and command-line mode operation. It is desired to eventually split these.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  First abstraction
&lt;/h3&gt;

&lt;p&gt;The section of code that did extended enumerations was abstracted to a module, which consolidated the repetitious code from across the multiple extended enumerations. This could be given a keycode for each enumeration - actually using its class as the id - and then be fed all the extended attributes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Second abstraction
&lt;/h3&gt;

&lt;p&gt;Here, the goal is the do a similar abstraction for application settings.&lt;/p&gt;

&lt;p&gt;The catch though, is how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;not have to specify a lot of the enumeration extensions twice&lt;/li&gt;
&lt;li&gt;abstract the way that the initial stage &lt;strong&gt;settings&lt;/strong&gt; code, &lt;em&gt;knew&lt;/em&gt; about all the custom enumerations used throughout the program&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Python variables
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Named Tuples
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;_nmdtpl_XenumAttrbt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fi_namedtuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nmdtpl_XenumAttrbt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;XenumJval XenumBrief XenumShow XenumSort&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where those elements are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;XenumJval = JSON value string&lt;/li&gt;
&lt;li&gt;XenumBrief = abbreviation&lt;/li&gt;
&lt;li&gt;XenumShow = longer display (but not a full description, that's what documentation is for)&lt;/li&gt;
&lt;li&gt;XenumSort = used to specify placement in GUI selection
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;nmdtpl_SettingAttrbt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fi_namedtuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nmdtpl_SettingAttrbt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;EnumMmb_DataType EnumTyp_Enum Value_Default Str_JSONkey Str_Show Fn_Val2EnumMmb Fn_EnumMmb2Val&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where those elements are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EnumMmb_DataType  = integer, string, list of string, custom enumeration&lt;/li&gt;
&lt;li&gt;EnumTyp_Enum = the class name if a custom enumeration&lt;/li&gt;
&lt;li&gt;Value_Default = default value&lt;/li&gt;
&lt;li&gt;Str_JSONkey = key used in the JSON&lt;/li&gt;
&lt;li&gt;Str_Show =&lt;/li&gt;
&lt;li&gt;Fn_Val2EnumMmb = conversion function&lt;/li&gt;
&lt;li&gt;Fn_EnumMmb2Val = converse conversion function&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Dictionaries
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;a first dictionary of the extended enumerations, keyed by the enumeration class, then each value being another dictionary keyed on the enumeration members (i.e. the run-time internal Python items) with each holding a &lt;code&gt;_nmdtpl_XenumAttrbt&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a second dictionary of the settings, keyed by an enumeration passed in when creating the dictionary. for each key is held a &lt;code&gt;nmdtpl_SettingAttrbt&lt;/code&gt; where the use of a value in &lt;code&gt;EnumTyp_Enum&lt;/code&gt; must have that already present in the first dictionary&lt;/li&gt;
&lt;li&gt; a third object, which gets passed the second dictionary and which adds a third dictionary of boolean flags for each setting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the providing abstraction, the first and second dictionaries will be inside an object each. While an application only needs one of each of those, it could perhaps do more of them. For example, settings being read from JSON could be done with a new object and then only merged to (or perhaps replacing) the "real" one if the load has no errors. Such things can then be implemented as application features that the service module has no "awareness" about.&lt;/p&gt;

</description>
      <category>python</category>
    </item>
    <item>
      <title>Running Linux purely on a USB drive</title>
      <dc:creator>geraldew</dc:creator>
      <pubDate>Wed, 11 Jun 2025 13:43:35 +0000</pubDate>
      <link>https://forem.com/geraldew/running-linux-purely-on-a-usb-drive-3mfg</link>
      <guid>https://forem.com/geraldew/running-linux-purely-on-a-usb-drive-3mfg</guid>
      <description>&lt;p&gt;Some time ago, I wrote an article about the method I use to run my computers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/geraldew/ubuntu-linux-installation-to-a-usb-external-drive-with-efi-boot-46pj"&gt;Linux installation to a USB external drive with EFI boot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Article Now
&lt;/h2&gt;

&lt;p&gt;The article I wrote was really just a technical annotation for other "tech" people to read, and wasn't intended as a "how to" suitable for non-technical people.&lt;/p&gt;

&lt;p&gt;However it seems timely to make something more general, perhaps to assist people seeking a safe way to try their hand at really using Linux, but with no modifications to their existing setup.&lt;/p&gt;

&lt;p&gt;But that still means this is not a set of instructions for the tech-unskilled to follow.&lt;/p&gt;

&lt;p&gt;Instead, this is something to share with such people, so they can pass it to friends who maybe do have the skills to help them out.&lt;/p&gt;

&lt;h2&gt;
  
  
  What My Method Achieves
&lt;/h2&gt;

&lt;p&gt;Which is to say that once setup, my method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;does not require modifying the existing setup of a computer (* see pedantic footnotes);&lt;/li&gt;
&lt;li&gt;runs from a physically small SSD USB drive;&lt;/li&gt;
&lt;li&gt;can be used on any generally current computer - something with a 64bit AMD/Intel CPU and supporting UEFI&lt;/li&gt;
&lt;li&gt;thereby, the little drive effectively IS your active system, the computer becomes just a commodity item&lt;/li&gt;
&lt;li&gt;indeed it inherently means you can shutdown and unplug, then plug into another computer and boot on that instead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've been running my systems this way for a very long time now. I was certainly operating thus when I moved to Melbourne in early 2010. I was doing so in the days of USB2, and USB3 has merely made it even more viable.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One caveat to make up front: I have assumed that it will be possible for the end user to do run-time selection of how to boot their computer. You could argue that it would be wise to confirm that before building the Linux setup on a USB drive. I can agree, but figure it &lt;em&gt;should usually&lt;/em&gt; be possible, but don't think it's worth trying to have an ordinary user determine this. Firmware and/or boot arrangements on computers is frustratingly non-standard and hard to talk about or describe.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Steps High Level
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Buy or get USB drives - one "thumb/flash" for the process, one "SSD" as the end goal&lt;/li&gt;
&lt;li&gt;Make an installation USB thumb/flash drive&lt;/li&gt;
&lt;li&gt;Build Linux onto the USB SSD&lt;/li&gt;
&lt;li&gt;Make that Linux fully portable&lt;/li&gt;
&lt;li&gt;Make sure the end-user, on the end-computer, has a way to select a UEFI device from which to boot.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Steps Mid Level
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Buy or get USB drives (showing minimum sizes)

&lt;ul&gt;
&lt;li&gt;a 4GB thumb/flash drive&lt;/li&gt;
&lt;li&gt;a 200GB USB SSD&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Make an installation USB thumb/flash drive

&lt;ul&gt;
&lt;li&gt;download an installer "image" file&lt;/li&gt;
&lt;li&gt;install/run a USB etcher program&lt;/li&gt;
&lt;li&gt;"burn" the flash drive&lt;/li&gt;
&lt;li&gt;perhaps: test that it can be booted&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Make the Linux

&lt;ul&gt;
&lt;li&gt;Obtain/use a computer with no internal hard drive (is simpler this way)&lt;/li&gt;
&lt;li&gt;boot the installation USB flash drive&lt;/li&gt;
&lt;li&gt;plug in the target USB SSD drive&lt;/li&gt;
&lt;li&gt;run the installation (as the SSD is the only viable target, this will be simple)&lt;/li&gt;
&lt;li&gt;shutdown&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Make the Linux fully portable

&lt;ul&gt;
&lt;li&gt;with the machine still having no internal drive, boot the USB SSD&lt;/li&gt;
&lt;li&gt;open a terminal window&lt;/li&gt;
&lt;li&gt;give the command to reinstall GRUB in portable mode: &lt;code&gt;sudo grub-install --removable&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;shutdown&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Make sure the end-user, on the end-computer, has a way to select a UEFI device from which to boot.

&lt;ul&gt;
&lt;li&gt;Optional: explore if their computer can be told to boot the USB SSD if it is plugged, but to otherwise boot its internal drive&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Steps Low Level
&lt;/h2&gt;

&lt;p&gt;Thankfully, there are quite a few videos and articles around on how to install Linux by using a flash drive as the installation media.&lt;/p&gt;

&lt;p&gt;After uploading this article, I will seek to add a series of hyperlinks from here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Installing Using a Computer With No Internal Drive?
&lt;/h3&gt;

&lt;p&gt;As noted in my other article, there are certainly other ways to do it. I just find this way a lot easier, there is less that can go wrong, and it guarantees that the unplugged drive has not been affected.&lt;/p&gt;

&lt;p&gt;But also, writing instructions for the alternatives could/would get very complex and hard to cover thoroughly.&lt;/p&gt;

&lt;p&gt;I happen to be lucky, that one of my laptops makes this very easy.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Secret Sauce
&lt;/h3&gt;

&lt;p&gt;I think it's disappointing that I need to write these articles at all, as there's no good reason the "Make the Linux fully portable" step could not be incorporated into the stock installers of the major distributions.&lt;/p&gt;

&lt;p&gt;However, as noted above, making an installation process safely handle all the possible/likely situations is easy for me to carp about when I'm not the one trying to make that.&lt;/p&gt;

&lt;p&gt;Sure, in ideal situations, such as forced by my "no internal drive" advice means the secret sauce is merely:&lt;br&gt;
    - &lt;code&gt;sudo grub-install --removable&lt;/code&gt;&lt;br&gt;
but even that needs to be done at the right point in the process.&lt;/p&gt;

&lt;p&gt;Why bother with this? In short, because if you don't, then there is a good chance that the next time your Linux-on-USB-drive does a kernel update, that will mess with the UEFI of the computer it is running on at the time. Technically this is about there being more than one ESP partition and this "secret sauce" avoids modifying the wrong one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linux Distro Variations
&lt;/h3&gt;

&lt;p&gt;Frankly, this is just an Ubuntu/Xubuntu user sharing a tip. I expect this will work for any distro that sets up GRUB, but I'm really not in any position to test or advise.&lt;/p&gt;

&lt;p&gt;This is also why I don't give any advice on how to do the partitioning - with just one drive present, the distro installer defaults will probably be fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pedantic Footnotes
&lt;/h2&gt;

&lt;p&gt;At the top of this article, I said my method "does not require modifying the existing setup of a computer". But quite how true that is, may depend on details of the existing setup. And we'll assume here that we mean a Windows setup, probably as had been originally supplied on a computer.&lt;/p&gt;

&lt;p&gt;It might be that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the firmware (often called "the BIOS") may be set with something called "secure boot" turned on.&lt;/li&gt;
&lt;li&gt;a setting may be suppressing an option during booting that lets a user select a boot option&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In general, as long as you can press a key as the computer boots and thereby get an ability to tell it which way to boot, then I consider the problem solved.&lt;/p&gt;

&lt;p&gt;So far, I have always found a way around problem firmware settings, but sorting them out does sometimes require mucking about and/or research. Just why this can be hard is a whole other topic.&lt;/p&gt;

&lt;p&gt;It is a fair thing to want things to be more automatic - for example, on the computers I mainly use, if I plug in the USB drive before powering on, then it will boot on that. But if I don't plug it in,then it will just boot as it used to.&lt;/p&gt;

&lt;p&gt;However, one of the computers here - a Lenovo - seems to refuse to allow such a default, and so I always have to do something manual as I power it up.&lt;/p&gt;

&lt;p&gt;There are ways around this, but they are out of the scope of this article, as the idea here is &lt;em&gt;to leave the computer itself **essentially unchanged&lt;/em&gt;**.&lt;/p&gt;

&lt;p&gt;Another thing I'm considering out of scope is the issue of the booted Linux being able to read (or write?) the files on the Windows setup. There are enough details and issues about that to warrant separate coverage - and personally, is not something I do, so I'm not properly knowledgeable on that score.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Character Encoding with the Python os module and Unicode</title>
      <dc:creator>geraldew</dc:creator>
      <pubDate>Sat, 05 Oct 2024 16:23:36 +0000</pubDate>
      <link>https://forem.com/geraldew/character-encoding-with-the-python-os-module-and-unicode-26f2</link>
      <guid>https://forem.com/geraldew/character-encoding-with-the-python-os-module-and-unicode-26f2</guid>
      <description>&lt;p&gt;I realised today that I hadn't published some notes I made about how:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python, and&lt;/li&gt;
&lt;li&gt;its "os" module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;handle unspecified character encodings.&lt;/p&gt;

&lt;p&gt;This was something I had to tackle when getting my program Foldatry to handle old file systems.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note that this was only about file and folder names, I've not yet checked how it goes for file contents.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My decisions were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to let the Python os module get names from the file system and put them into a Python Unicode string - i.e. that I wouldn't write code to tell it how to interpret the encoding of the names until &lt;strong&gt;after&lt;/strong&gt; it had stored them in Python variables;&lt;/li&gt;
&lt;li&gt;to write the following function for use in displaying path-file-names (to the terminal or to screen widgets) - this was vital because otherwise the program would simply crash.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pfn4print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;p_pfn&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;p_pfn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;surrogateescape&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cp1252&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;backslashreplace&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that cp1252 is the Windows-1252 superset of the ISO-8859-1 encoding standard.&lt;/p&gt;

&lt;p&gt;The rationale for that code is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the Python os module will by-default have put the bytes it got from the file system into a Python Unicode string;&lt;/li&gt;
&lt;li&gt;we then want to turn that back into the original byte sequence, which is achieved by encode('utf-8', 'surrogateescape')&lt;/li&gt;
&lt;li&gt;this works because the documentation for the os module says that's what was done to make the Python Unicode string i.e. that it used 'surrogateescape' &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then for display it is using the Windows-1252 codec for two reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it was the most common for a long time: longer than the earlier MS-DOS sets and than other language sets;&lt;/li&gt;
&lt;li&gt;because it has representations for nearly all the 256 characters of the 8-bit byte patterns, so it should usually show something even if it's not what was originally intended.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The alternative to this would have been to always handle all the path and filenames as bytestrings, and that seemed too much work.&lt;/p&gt;

&lt;p&gt;Now if you know lots about Unicode, you may know that not all bytes sequences are valid in its standard encodings.&lt;/p&gt;

&lt;p&gt;So what does Python the os module do when it encounters these (i.e. in byte sequences that it didn't get told how to decode)? &lt;/p&gt;

&lt;p&gt;Well, this is where 'surrogateescape' comes in, as this effectively has Python store the bad byte sequence as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it wasn't valid; and&lt;/li&gt;
&lt;li&gt;what its sequence was.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which is why &lt;code&gt;encode('utf-8', 'surrogateescape')&lt;/code&gt; gives us back the original bytes.&lt;/p&gt;

&lt;p&gt;Now I do grant that this seems a bit "magic". While you certainly could look deeper into how:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python handles Unicode internally;&lt;/li&gt;
&lt;li&gt;as well as all the different encodings it knows about;&lt;/li&gt;
&lt;li&gt;and all the options available as it encodes and decodes;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;but my guess is that you can mostly get-by without going that deep, and just trusting "surrogateescape" to do its trick.&lt;/p&gt;

&lt;p&gt;Note that this is all for &lt;em&gt;program doesn't crash&lt;/em&gt; handling. Ultimately, correct handling of unspecified character encoding is a matter of working out which encoding was/is valid for it. There are tools which will take a good go at doing that, but to be sure of it will require human judgement - essentially because it got there by human misadventure.&lt;/p&gt;

&lt;p&gt;p.s. here's function that converts a string to a list of its Unicode ordinals (i.e. the sequence of code points). Handy for checking what Unicode string Python really thinks it has.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;string_as_list_of_code_points&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;p_str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a_char&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a_char&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example 1
&lt;/h2&gt;

&lt;p&gt;This comes from a CD-ROM that I'd made back in the Windows 98 era. As a consequence, the filenames definitely were not done as Unicode.&lt;/p&gt;

&lt;p&gt;Our example filename here is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;für&lt;/code&gt;
where that letter in the middle is the "LATIN SMALL LETTER U WITH DIAERESIS"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Encodings
&lt;/h3&gt;

&lt;p&gt;On the CD-ROM the encoding is the sequence of these three bytes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0x66&lt;/code&gt; &lt;code&gt;0xFC&lt;/code&gt; &lt;code&gt;0x72&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read into Python3 -via the &lt;code&gt;os&lt;/code&gt; module - it becomes this sequence of Unicode code points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;U+0066&lt;/code&gt;  &lt;code&gt;U+FFFD!!FC&lt;/code&gt;  &lt;code&gt;U+0072&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we succeed with conversion - see below - then Python holds this sequence of Unicode "code points":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;U+0066&lt;/code&gt;  &lt;code&gt;U+00FC&lt;/code&gt;  &lt;code&gt;U+0072&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If Python writes that out to UTF-8 it becomes these four bytes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0x66&lt;/code&gt; &lt;code&gt;0xC3&lt;/code&gt; &lt;code&gt;0xBC&lt;/code&gt; &lt;code&gt;0x72&lt;/code&gt; 
being: a one-byte character, a two-byte character, and a one-byte character.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My understanding of what Windows-NT uses internally, is "UCS-2" which in bytes, would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0x00&lt;/code&gt;  &lt;code&gt;0x66&lt;/code&gt;  &lt;code&gt;0x00&lt;/code&gt;  &lt;code&gt;0xFC&lt;/code&gt;  &lt;code&gt;0x00&lt;/code&gt;  &lt;code&gt;0x72&lt;/code&gt;
being three two-byte characters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.unicodepedia.com/unicode/latin-1-supplement/fc/latin-small-letter-u-with-diaeresis/" rel="noopener noreferrer"&gt;LATIN SMALL LETTER U WITH DIAERESIS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;from the Unicode group: Latin-1 Supplement&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Python sequence
&lt;/h3&gt;

&lt;p&gt;Okay, let's see how that works in a Python interactive session. The following was clipped over from a terminal window.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python3
Python 3.10.12 (main, Sep 11 2024, 15:47:36) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
&amp;gt;&amp;gt;&amp;gt; s_0 = "für"
&amp;gt;&amp;gt;&amp;gt; print( list( ord(a_char) for a_char in list(s_0) ) )
[102, 252, 114]
&amp;gt;&amp;gt;&amp;gt; ba_1 = s_0.encode( "utf-8", "surrogateescape")
&amp;gt;&amp;gt;&amp;gt; print( list( ba_1 ) )
[102, 195, 188, 114]
&amp;gt;&amp;gt;&amp;gt; ba_2 = s_0.encode("cp1252", "backslashreplace")
&amp;gt;&amp;gt;&amp;gt; print( list( ba_2 ) )
[102, 252, 114]
&amp;gt;&amp;gt;&amp;gt; s_1 = ba_2.decode("utf-8", "surrogateescape")
&amp;gt;&amp;gt;&amp;gt; print( list( ord(a_char) for a_char in list(s_1) ) )
[102, 56572, 114]
&amp;gt;&amp;gt;&amp;gt; ba_3 = s_1.encode( "utf-8", "surrogateescape")
&amp;gt;&amp;gt;&amp;gt; print( list( ba_2 ) )
[102, 252, 114]
&amp;gt;&amp;gt;&amp;gt; s_2 = ba_3.decode("cp1252")
&amp;gt;&amp;gt;&amp;gt; print( list( ord(a_char) for a_char in list(s_2) ) )
[102, 252, 114]
&amp;gt;&amp;gt;&amp;gt; print( s_2)
für

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To recap that relative to the earlier commentary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ba_2 is the way this string would have been on the CD-ROM&lt;/li&gt;
&lt;li&gt;s_1 is what happens when we ask Python &lt;code&gt;os&lt;/code&gt; to read ba_2 without specifying an encoding - so it presumes UTF-8 but then has to cope with the fact that 252 is not valid as the first byte of a UTF-* character, so &lt;code&gt;os&lt;/code&gt; stores it as a surrogate&lt;/li&gt;
&lt;li&gt;we can see how it is properly encoded as UTF-8 in ba_1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depending on your familiarity with code points and encoding, there might be two surprises in that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;that UTF-8 encoding of three characters required four bytes - essentially because the "ü" required two bytes&lt;/li&gt;
&lt;li&gt;the matter of "things that wouldn't be valid in UTF-8" then being handled in a strange way, but that can be reversed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Similarly, something that might &lt;strong&gt;not&lt;/strong&gt; catch your attention, is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;that this example is still &lt;em&gt;simple&lt;/em&gt; because the archaic Windows-1252 byte encoding for "ü" matches its Unicode code point number: 252&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oh, and:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I don't actually remember where I got the notation &lt;code&gt;U+FFFD!!FC&lt;/code&gt;  from.&lt;/li&gt;
&lt;li&gt;I might have even made it up, as a hybrid way to say "will show as the Unicode Replacement Character but knows it came from hex FC"&lt;/li&gt;
&lt;li&gt;If you look at the Python session, you'll see code point in the middle of s_1 is decimal 56572 which is 0xDCFC hexadecimal&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example 2
&lt;/h2&gt;

&lt;p&gt;So now let's look at an example, where the Windows-1252 byte encoding byte for a character does &lt;strong&gt;not&lt;/strong&gt; match its Unicode code point number.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the euro symbol&lt;/li&gt;
&lt;li&gt;&lt;code&gt;€30&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;in Windows-1252 this is encoded as &lt;code&gt;0x80&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;in Unicode this is code point U+20AC (decimal 8364)&lt;/li&gt;
&lt;li&gt;(in case you're wondering, this difference is because Microsoft decided to use places in the "C1 control codes" range, that Unicode chose to leave alone)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.unicodepedia.com/unicode/currency-symbols/20ac/euro-sign/" rel="noopener noreferrer"&gt;Euro Sign&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Python sequence
&lt;/h3&gt;

&lt;p&gt;Here is the same sequence of actions as we did for Example 1 but this time with a short string starting with the Euro symbol.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; s_0 = "€30"
&amp;gt;&amp;gt;&amp;gt; print( list( ord(a_char) for a_char in list(s_0) ) )
[8364, 51, 48]
&amp;gt;&amp;gt;&amp;gt; ba_1 = s_0.encode( "utf-8", "surrogateescape")
&amp;gt;&amp;gt;&amp;gt; print( list( ba_1 ) )
[226, 130, 172, 51, 48]
&amp;gt;&amp;gt;&amp;gt; ba_2 = s_0.encode("cp1252", "backslashreplace")
&amp;gt;&amp;gt;&amp;gt; print( list( ba_2 ) )
[128, 51, 48]
&amp;gt;&amp;gt;&amp;gt; s_1 = ba_2.decode("utf-8", "surrogateescape")
&amp;gt;&amp;gt;&amp;gt; print( list( ord(a_char) for a_char in list(s_1) ) )
[56448, 51, 48]
&amp;gt;&amp;gt;&amp;gt; ba_3 = s_1.encode( "utf-8", "surrogateescape")
&amp;gt;&amp;gt;&amp;gt; print( list( ba_2 ) )
[128, 51, 48]
&amp;gt;&amp;gt;&amp;gt; s_2 = ba_3.decode("cp1252")
&amp;gt;&amp;gt;&amp;gt; print( list( ord(a_char) for a_char in list(s_2) ) )
[8364, 51, 48]
&amp;gt;&amp;gt;&amp;gt; print( s_2)
€30

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Things to note this time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ba_1 is the UTF-8 encoding, note that the Euro symbol requires &lt;em&gt;three&lt;/em&gt; bytes&lt;/li&gt;
&lt;li&gt;ba_2 is the way this string would have been in Windows 98 et al&lt;/li&gt;
&lt;li&gt;s_1 is what happens when we ask Python &lt;code&gt;os&lt;/code&gt; to read ba_2 without specifying an encoding - so it presumes UTF-8 but then has to cope with what it gets&lt;/li&gt;
&lt;li&gt;we still have the matter of "things that wouldn't be valid in UTF-8" being handled in a strange way, but that can be reversed&lt;/li&gt;
&lt;li&gt;you'll see this time, the code point at the front of s_1 is decimal 56448 which is 0xDC80 hexadecimal&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  End note
&lt;/h2&gt;

&lt;p&gt;I should perhaps add that I'm not claiming the methods shown here are either complete or bulletproof. This is merely what I added to my program to cope with what it was encountering. I do plan to revisit all this at some later date but there's a lot of important other features that will come first. So, not "real soon now" at all.&lt;/p&gt;

</description>
      <category>python</category>
      <category>unicode</category>
    </item>
    <item>
      <title>Changing SQL Dialect From Teradata To SQLite</title>
      <dc:creator>geraldew</dc:creator>
      <pubDate>Mon, 03 Jun 2024 14:59:38 +0000</pubDate>
      <link>https://forem.com/geraldew/changing-sql-dialect-from-teradata-to-sqlite-i23</link>
      <guid>https://forem.com/geraldew/changing-sql-dialect-from-teradata-to-sqlite-i23</guid>
      <description>&lt;h2&gt;
  
  
  Source of This Annotation
&lt;/h2&gt;

&lt;p&gt;I recently had the occasion to convert an interesting SQL script of mine from being in Teradata SQL to running in SQLite. As I did so, I made notes of the various specific changes I had to make. From those, I have plucked out the concepts and some examples and then added annotations.It is not a master guide for the topic, rather is just some sharing of a single experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment
&lt;/h2&gt;

&lt;p&gt;Some of the issues encountered are probably &lt;strong&gt;not&lt;/strong&gt; about the difference in dialect, but are instead just that my place in the two environments are quite different. For Teradata the environment is mostly not in my control. For SQLite, while it is completely in my control, I've not added any elements that might make it more equivalent - largely because the intention is to easy to replicate.&lt;/p&gt;

&lt;p&gt;The most immediate aspect of this is that of namespaces, where for Teradata I must use multiple namespaces as forced by system-wide settings. For SQLite everything happens inside a single namespace. These issues will be explicitly apparent in the text below, but it will &lt;strong&gt;not&lt;/strong&gt; call out which are due to the dialect difference and which to the environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Details
&lt;/h2&gt;

&lt;p&gt;Enough preamble - into the details we go. These are not in any specific order, as this has merely been adapted from run-and-hit-error approach to making all the required changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Table names
&lt;/h3&gt;

&lt;p&gt;While in some senses obvious - that in changing from one platform to another, that you might not have exactly the same named tables present - another aspect is that the whole structure of name referencing can be different.&lt;br&gt;
From&lt;br&gt;
&lt;code&gt;DatabaseName.TableName&lt;/code&gt;&lt;br&gt;
To&lt;br&gt;
&lt;code&gt;TableName&lt;/code&gt;&lt;br&gt;
However, that kind of change was going to cause problems for situations where I had actually made use of the different databases as name spaces.&lt;/p&gt;

&lt;p&gt;For example, it happens that where I work, I don't have system permissions to create views and macros in the same databases where I can make tables. And while that is a hassle and requires me to instead make any Views and Macros in my own "user" database (because in Teradata, a user account IS a user's own database) that is also handy.&lt;/p&gt;

&lt;p&gt;For example, I used my own space as a place to create a view&lt;br&gt;
&lt;code&gt;MyUser.NameOfPurpose&lt;/code&gt;&lt;br&gt;
and then use it to make a table of the same name but different &lt;em&gt;database location&lt;/em&gt; as in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt;
    &lt;span class="n"&gt;DatabaseName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NameOfPurpose&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
    &lt;span class="n"&gt;MyUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NameOfPurpose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is nice and neat, but then poses a problem if we're translating everything to a single database/namespace.&lt;/p&gt;

&lt;p&gt;I'm not saying the following is a great strategy, but it was my quick &lt;em&gt;during the process&lt;/em&gt; choice to make use of prefixes in lieu of namespaces and it got me through this time.&lt;/p&gt;

&lt;p&gt;So I would change from&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MyUser.Something&lt;/code&gt;
To&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;VEW_Something&lt;/code&gt;
and from
&lt;code&gt;WorkingDatabase.MyUser_Something&lt;/code&gt; (because a polite convention where I work is to put our usernames as a prefix*)
To
&lt;code&gt;TBL_Something&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I was going to do more of this, then I might come up with something more sophisticated.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(* by the way, the politeness is to not make other analysts look up the data dictionary to get the &lt;code&gt;CREATORNAME&lt;/code&gt; and also has a nice effect that our general object names can't interfere with each other)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ANZSIC Resource changes
&lt;/h3&gt;

&lt;p&gt;This section is unique to the specific purpose I had for this conversion, which was that is was using the &lt;a href="https://www.abs.gov.au/statistics/classifications/australian-and-new-zealand-standard-industrial-classification-anzsic/2006-revision-2-0"&gt;Australian and New Zealand Standard Industrial Classification (ANZSIC)&lt;/a&gt; codes. Follow that link if you want to know more about these. My understanding is that while an independent standard used across Australia and New Zealand, there are similar things in other parts of the world.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;And indeed I've been using these kinds of codes in my data work for over 20 years.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As the whole purpose of the SQL script I was converting is to do &lt;em&gt;something interesting&lt;/em&gt; with data classified using these codes, necessarily I also had to ensure the handling of these codes was correctly adapted from one environment to the other.&lt;/p&gt;

&lt;p&gt;I will leave this section here, it may still be of interest as the kind of changes that occur in this type of exercise.&lt;/p&gt;

&lt;p&gt;Things I had to deal with included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduce from 5 digit coding to just 4&lt;/li&gt;
&lt;li&gt;Specific table and column names&lt;/li&gt;
&lt;li&gt;Change some specific ANZSIC codes&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Reduce from 5 digit coding to just 4
&lt;/h4&gt;

&lt;p&gt;As it happens, in my workplace, we enhance some ANZSIC codes by adding an extra digit. In picking up both the ANZSIC definitions and some example data from public "open data" resources, these use only the four digit codes, some various changes had to be made to suit that.&lt;/p&gt;

&lt;h4&gt;
  
  
  Specific table and column names
&lt;/h4&gt;

&lt;p&gt;Similarly, at my workplace, the reference tables for the ANZSIC codes were made for me by other people (and had the added 5th digit) so in downloading them independently from the ABS (Australian Bureau of Statistics) I don't have the same table structures on tap.&lt;/p&gt;

&lt;p&gt;As it happened, this could have given me the chance to do something more appropriate in terms of the structures I would make but didn't want to be rewriting a lot of things while I was mainly just trying to adapt some 2,000 lines of SQL. So I chose a compromise, loading the lookup tables in a way that was convenient to construct from the downloads but then using a custom view to emulate the structure assumed by the script I was converting.&lt;/p&gt;

&lt;h4&gt;
  
  
  Change some specific ANZSIC codes
&lt;/h4&gt;

&lt;p&gt;As the script that I was converting has a special feature of being able to isolate a single (or small set of) ANZSIC codes for treatment, I didn't have any wish to expose which specific ones my workplace had a special interest in. So I chose another one that seemed to have a similar distribution property (in totally different data, mind).&lt;/p&gt;

&lt;p&gt;So that meant changing each place where the script had (the &lt;em&gt;secret&lt;/em&gt; digit sequence):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;'NNNN'
and replacing it with&lt;/li&gt;
&lt;li&gt;'8512'
Note that those are both strings/chars because while many of the ANZSIC codes are digits, not all are.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Making Views Syntax Change
&lt;/h3&gt;

&lt;p&gt;On Teradata you can make a View using either the keyword &lt;code&gt;CREATE&lt;/code&gt; or the keyword &lt;code&gt;REPLACE&lt;/code&gt; - with the latter working even when a view of that name already exists. As a consequence there is no reason not to always use &lt;code&gt;REPLACE&lt;/code&gt;. But SQLite doesn't have this feature.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FWIW by contrast, Teradata SQL &lt;em&gt;still&lt;/em&gt; doesn't have a form of &lt;code&gt;DROP&lt;/code&gt; that is safe to use regardless of whether an item does or does not already exist. Yes, there are some work-arounds but that's a whole other topic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, a first step is to search/replace throughout the script to find every&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;REPLACE VIEW&lt;/code&gt;
and change it to&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CREATE VIEW&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Actually, that's not quite enough, because in part, one writes scripts so they can be run and re-run. For SQLite this is a simple matter of putting a bulletproof &lt;code&gt;DROP&lt;/code&gt; before each &lt;code&gt;CREATE&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;NameOfView&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;NameOfView&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Making Macros Significant Change
&lt;/h3&gt;

&lt;p&gt;While noting that, like with views, for its macro syntax Teradata allows a &lt;code&gt;REPLACE MACRO&lt;/code&gt; as well as a &lt;code&gt;CREATE MACRO&lt;/code&gt; - but that's not really the problem.&lt;/p&gt;

&lt;p&gt;Instead, the situation is that SQLite doesn't appear to have any "macro" feature. Luckily, that's not quite true.&lt;/p&gt;

&lt;p&gt;Because what SQLite does have, is triggers. And while a trigger has to be tied to some other database action, it provides a way of defining a statement that will execute.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Of course, a Teradata macro has quite a few other aspects, such as parameter passing, but in this case I didn't need any of that.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So for converting a Teradata Macro into SQL, what we do is create a Trigger for a view that does nothing. To execute the Trigger, we do a DELETE FROM for the view name. In SQLite the delete would fail on the view but still triggers the .. trigger to execute. Ergo, we have a kind of macro.&lt;/p&gt;

&lt;p&gt;So, if we had a macro named MacroName, then in Teradata SQL we would do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="n"&gt;MACRO&lt;/span&gt; &lt;span class="n"&gt;MacroName&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="c1"&gt;-- sql statements&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to get a similar effect in SQLite, we first create a dummy view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; 
  &lt;span class="n"&gt;View4Trigger_MacroName&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="k"&gt;CURRENT_TIME&lt;/span&gt; 
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then we define a trigger that will do our bidding&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt;
  &lt;span class="n"&gt;Trigger4_MacroName&lt;/span&gt;
&lt;span class="k"&gt;INSTEAD&lt;/span&gt; &lt;span class="k"&gt;OF&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt;
  &lt;span class="n"&gt;View4Trigger_MacroName&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="c1"&gt;-- sql&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then we trigger the trigger, with a statement that does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; 
  &lt;span class="n"&gt;View4Trigger_MacroName&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Whither Stored Procedures
&lt;/h4&gt;

&lt;p&gt;p.s. Who said "Stored Procedure" ? Actually, you won't find "macro" mentioned anywhere - and that's because macros are a Teradata oddity. There's a lot I could say about this, but it's just Teradata history and not particularly relevant here. Teradata did eventually add Stored Procedures to its feature set - quite a long time ago now - but macros are still there and have some permission conveniences.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remove Temporary Scaffolding Code
&lt;/h3&gt;

&lt;p&gt;This point merely says something about how I write Teradata SQL, which is that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I can be lazy about writing &lt;code&gt;CREATE&lt;/code&gt; statements and so will use a &lt;code&gt;CREATE .. AS&lt;/code&gt; construct to make a table and then use &lt;code&gt;SHOW TABLE&lt;/code&gt; to get a CREATE statement ready to adapt.&lt;/li&gt;
&lt;li&gt;but I also don't leave a script with a CREATE AS in it, and replace it with a &lt;code&gt;CREATE&lt;/code&gt; for making an empty table and then do an &lt;code&gt;INSERT&lt;/code&gt; to populate it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a consequence, I will end up with a script which included a whole bunch of &lt;code&gt;CREATE AS WITH NO DATA&lt;/code&gt; and &lt;code&gt;SHOW TABLE&lt;/code&gt; then &lt;code&gt;DROP TABLE&lt;/code&gt; statement combinations lying around.&lt;/p&gt;

&lt;p&gt;In the SQLite environment - and with the script feature complete - these were just extraneous and could be deleted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remove Character Set Declarations
&lt;/h3&gt;

&lt;p&gt;One of the consequences of my &lt;code&gt;CREATE AS&lt;/code&gt; and &lt;code&gt;SHOW TABLE&lt;/code&gt; method is that the table creations I thereby get will have all the specific defaults applied and made explicit. In the Teradata environment that's quite a plus.&lt;/p&gt;

&lt;p&gt;But they immediately caused me problems in SQLite and had to go.&lt;/p&gt;

&lt;p&gt;So any column definitions that had the following text&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CHARACTER SET LATIN NOT CASESPECIFIC&lt;/code&gt;
had to have them removed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Remove Set Table Declarations
&lt;/h3&gt;

&lt;p&gt;Along the same line as the above, the Teradata (good) default is to have "set" tables (as opposed to "multiset") but this is not a feature of SQLite and so had to go.&lt;br&gt;
Thus&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CREATE SET TABLE&lt;/code&gt;
becomes simply&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CREATE TABLE&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Remove More Table Declarations
&lt;/h3&gt;

&lt;p&gt;And to cut a long story short, for my script the following stock clause also had to be removed from &lt;code&gt;CREATE TABLE&lt;/code&gt; statements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;    &lt;span class="n"&gt;FALLBACK&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="k"&gt;NO&lt;/span&gt; &lt;span class="k"&gt;BEFORE&lt;/span&gt; &lt;span class="n"&gt;JOURNAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="k"&gt;NO&lt;/span&gt; &lt;span class="k"&gt;AFTER&lt;/span&gt; &lt;span class="n"&gt;JOURNAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;CHECKSUM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;MERGEBLOCKRATIO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="k"&gt;MAP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TD_MAP1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  UPI recoding
&lt;/h3&gt;

&lt;p&gt;Where &lt;code&gt;UPI&lt;/code&gt; = &lt;code&gt;UNIQUE PRIMARY INDEX&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In Teradata, the equivalent to a "primary key" is a "unique primary index". But as well as the keyword change, the SQLite syntax is different. In Teradata the UPI setting comes as a clause &lt;em&gt;after&lt;/em&gt; the list of column definitions has been closed - with a &lt;code&gt;)&lt;/code&gt; character. In SQLite, the primary key setting is done &lt;em&gt;inside&lt;/em&gt; the column list.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Actually in SQLite it has two forms of that but we'll only use one here.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;  &lt;span class="n"&gt;LastColumnName&lt;/span&gt; &lt;span class="n"&gt;DataType&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; 
  &lt;span class="n"&gt;PrimaryIndexColumn&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;  &lt;span class="n"&gt;LastColumnName&lt;/span&gt; &lt;span class="n"&gt;DataType&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; 
    &lt;span class="n"&gt;PrimaryIndexColumn&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SAMPLE and TOP
&lt;/h3&gt;

&lt;p&gt;This is yet another one of those things that simply varies among SQL dialects - and frankly I have no which one, if either, is &lt;strong&gt;in the SQL standard&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Teradata has a &lt;code&gt;SAMPLE&lt;/code&gt; clause, but as I was only using it for some minor data testing of view constructions, could be easily be replaced.&lt;/p&gt;

&lt;p&gt;Ditto for the Teradata syntax of using &lt;code&gt;SELECT TOP x&lt;/code&gt; at the beginning of a &lt;code&gt;SELECT&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;Both of those could be replaced by using the SQLite syntax of add &lt;code&gt;LIMIT x&lt;/code&gt; at the end of a &lt;code&gt;SELECT&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;While not important anywhere, this was annoying to enact as it couldn't be done by simple search/replace actions in the editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  No System Calendar Pseudo Table
&lt;/h3&gt;

&lt;p&gt;This is another small thing that I've become used to doing on Teradata, because it supplies a built-in pseudo table that generates a full calendar.&lt;/p&gt;

&lt;p&gt;While obviously useful for calendar related things, the fact that it has a &lt;code&gt;day_of_calendar&lt;/code&gt; column makes it easy to construct scratch tables as if out of thin air.&lt;/p&gt;

&lt;p&gt;In the script at hand I was using it as the base for some cross joins to generate a full set of digit combinations. As this was only a matter of ten rows - for the digits "0" to "9" - I chose to replace it with a real table and simply wrote ten insert to literally populate it with the desired digits.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt;
  &lt;span class="n"&gt;TBL_1_Digits&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; 
  &lt;span class="n"&gt;TBL_1_Digits&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;DigitChar&lt;/span&gt; &lt;span class="nb"&gt;CHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; 
    &lt;span class="n"&gt;DigitChar&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;TBL_1_Digits&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;DigitChar&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'0'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;TBL_1_Digits&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;DigitChar&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;TBL_1_Digits&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;DigitChar&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'2'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;TBL_1_Digits&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;DigitChar&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'3'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;TBL_1_Digits&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;DigitChar&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'4'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;TBL_1_Digits&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;DigitChar&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'5'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;TBL_1_Digits&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;DigitChar&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'6'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;TBL_1_Digits&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;DigitChar&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'7'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;TBL_1_Digits&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;DigitChar&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'8'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;TBL_1_Digits&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;DigitChar&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'9'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In retrospect that was probably a better solution all round - I'm used to using the Teradata &lt;code&gt;sys_calendar.CALENDAR&lt;/code&gt; for all kinds of things in many kinds of scales, so my general reasons for doing so remain valid.&lt;/p&gt;

&lt;h3&gt;
  
  
  Replace FULL OUTER JOIN
&lt;/h3&gt;

&lt;p&gt;As SQLite does not support &lt;code&gt;FULL OUTER JOIN&lt;/code&gt; then something will have to be done instead.&lt;/p&gt;

&lt;p&gt;As it happens, this is a well covered topic, with some opinions being that FULL OUTER JOIN is something that &lt;strong&gt;should be avoided&lt;/strong&gt; &lt;em&gt;even where it is supported&lt;/em&gt;. The stock advice seems to be to replace it with a combination of UNION and GROUP BY structures.&lt;/p&gt;

&lt;p&gt;Here is what I had as Teradata SQL&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Attempt a coalesce of the two lookup methods&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;AZ_ML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;At_Lvl_Cd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DC_AU&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DigitsN&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;At_Lvl_Cd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;AZ_ML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lvl_At&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;CHARACTER_LENGTH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;DC_AU&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DigitsN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Lvl_At&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;AZ_ML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Up_Lvl_Cd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DC_AU&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DigitsM&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Up_Lvl_Cd&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;DbName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyUser_ANZSIC_DigitCharsAndUps&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;DC_AU&lt;/span&gt;
  &lt;span class="k"&gt;FULL&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt;
  &lt;span class="n"&gt;DbName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyUser_ANZSIC_Multi_Level_Lookup_Base&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;AZ_ML&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt;
    &lt;span class="n"&gt;AZ_ML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;At_Lvl_Cd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DC_AU&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DigitsN&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is the SQLite replacement&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Attempt a coalesce of the two lookup methods&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;At_Lvl_Cd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lvl_At&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Lvl_At&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Up_Lvl_Cd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Up_Lvl_Cd&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="c1"&gt;-- U&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
      &lt;span class="n"&gt;AZ_ML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;At_Lvl_Cd&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;At_Lvl_Cd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;AZ_ML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lvl_At&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Lvl_At&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;AZ_ML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Up_Lvl_Cd&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Up_Lvl_Cd&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt;
      &lt;span class="n"&gt;TBL_ANZSIC_Multi_Level_Lookup_Base&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;AZ_ML&lt;/span&gt;
    &lt;span class="k"&gt;UNION&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
      &lt;span class="n"&gt;DC_AU&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DigitsN&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;At_Lvl_Cd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;LENGTH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;DC_AU&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DigitsN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Lvl_At&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;DC_AU&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DigitsM&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Up_Lvl_Cd&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt;
      &lt;span class="n"&gt;TBL_ANZSIC_DigitCharsAndUps&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;DC_AU&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
  &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;At_Lvl_Cd&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be frank, there is much more that can be said about &lt;code&gt;FULL OUTER JOIN&lt;/code&gt; versus &lt;code&gt;UNION .. GROUP BY&lt;/code&gt; but this was all I needed for the few places the issue was present in this script.&lt;/p&gt;

&lt;p&gt;There is plenty to read elsewhere on this topic.&lt;/p&gt;

&lt;h3&gt;
  
  
  CHARACTER_LENGTH
&lt;/h3&gt;

&lt;p&gt;While simple, this one caught me by surprise because I thought this function was part of the SQL "standard" - not that that ever means very much in practice. Anyway, the solution was a simple change of function name.&lt;/p&gt;

&lt;p&gt;I replaced&lt;br&gt;
&lt;code&gt;CHARACTER_LENGTH&lt;/code&gt;&lt;br&gt;
with&lt;br&gt;
&lt;code&gt;LENGTH&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  QUALIFY
&lt;/h3&gt;

&lt;p&gt;Teradata is one of the dialects that has &lt;code&gt;QUALIFY&lt;/code&gt; and uses it to enable filtering in the same query layer as a "window" function is declared. In short, SQLite does not have this.&lt;/p&gt;

&lt;p&gt;In my case, this is not new, as I've previously had to re-code from Teradata to HiveQL, which is similar is having window functions but not a "QUALIFY" clause.&lt;/p&gt;

&lt;p&gt;What it requires is adding an extra layer of table abstraction - as a derived table or a CTE (common table expression) and then use a WHERE clause to do what the QUALIFY did.&lt;/p&gt;

&lt;p&gt;For example, here is how it might look in Teradata SQL&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
    &lt;span class="n"&gt;TheTableName&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;
&lt;span class="n"&gt;QUALIFY&lt;/span&gt;
    &lt;span class="n"&gt;ROW_NUMBER&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; 
        &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; 
            &lt;span class="n"&gt;GroupCol&lt;/span&gt; 
        &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;By&lt;/span&gt; 
            &lt;span class="n"&gt;Sortcol&lt;/span&gt; 
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a first step to conversion is to make the window function a named element in the SELECT clause. Note that Teradata is happy to apply the QUALIFY to that named value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ROW_NUMBER&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; 
        &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; 
            &lt;span class="n"&gt;GroupCol&lt;/span&gt; 
        &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;By&lt;/span&gt; 
            &lt;span class="n"&gt;Sortcol&lt;/span&gt; 
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Rw_Num&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
    &lt;span class="n"&gt;TheTableName&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;
&lt;span class="n"&gt;QUALIFY&lt;/span&gt;
  &lt;span class="n"&gt;Rw_Num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can abstract all of the above and remove the QUALIFY and instead do the same filtering as a WHERE clause.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;D_T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="c1"&gt;-- D_T&lt;/span&gt;
        &lt;span class="k"&gt;SELECT&lt;/span&gt;
            &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ROW_NUMBER&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; 
                &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; 
                    &lt;span class="n"&gt;GroupCol&lt;/span&gt; 
                &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;By&lt;/span&gt; 
                    &lt;span class="n"&gt;Sortcol&lt;/span&gt; 
                &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Rw_Num&lt;/span&gt;
        &lt;span class="k"&gt;FROM&lt;/span&gt;
            &lt;span class="n"&gt;TheTableName&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;D_T&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;D_T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rw_Num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the QUALIFY gone, the syntax is now ready to work in SQLite.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do note that I'm not saying this will be internally planned and executed the exact same way - or that it won't, that being a complex per-platform topic. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Change from PRIMARY INDEX
&lt;/h3&gt;

&lt;p&gt;Now, you might be excused for thinking this was already covered in the text above. But no, this is another twist, because it is UNIQUE PRIMARY INDEX which is the PRIMARY KEY equivalent.&lt;/p&gt;

&lt;p&gt;Instead, in Teradata, a non-unique "PRIMARY INDEX" merely assists with data spreading at execution. While this technically means that the clause can just be dropped without having any effect, there's a good chance that the reason it was there will imply some thinking about what should be done instead.&lt;/p&gt;

&lt;p&gt;In the one case of this for the conversion I was attempting I had used "PRIMARY INDEX" simply because I was too lazy to work out what column combination would be a UPI (i.e. primary key). Having re-assessed that, I made a useful selection and set a new PRIMARY KEY clause.&lt;/p&gt;

&lt;h3&gt;
  
  
  Change Syntax for Defining a Recursive View
&lt;/h3&gt;

&lt;p&gt;While this was confusing to sort out, from comparative reading of documentation and examples, the change is simple enough - albeit with a couple of twists. Indeed, there were three issues to be dealt with here. Do note: I'm not going to try to explain recursive queries in this context - if you need to gain comfort with those then you will need to seek elsewhere.&lt;/p&gt;

&lt;p&gt;The three issues are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Syntax&lt;/li&gt;
&lt;li&gt;Final Select&lt;/li&gt;
&lt;li&gt;Naming&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Syntax
&lt;/h4&gt;

&lt;p&gt;On the face of it, it mainly looks like a change from the Teradata SQL&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;RECURSIVE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt;
  &lt;span class="n"&gt;VEW_ANZSIC_Multi_Level_Recursive_Lookup&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;At_Lvl_Cd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Lvl_At&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Up_Lvl_Cd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Up_Lvl_At&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;DegreeOfSep&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To the SQLite form of&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; 
  &lt;span class="n"&gt;VEW_ANZSIC_Multi_Level_Recursive_Lookup&lt;/span&gt; 
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;RECURSIVE&lt;/span&gt; 
  &lt;span class="n"&gt;Recursive_Lookup&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;At_Lvl_Cd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Lvl_At&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Up_Lvl_Cd&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Up_Lvl_At&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;DegreeOfSep&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But as we will see, there is more to it than this.&lt;/p&gt;

&lt;h4&gt;
  
  
  Final Select
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE RECURSIVE VIEW NameOfView ( ListOfColumns) AS ( 
-- Seed Select Statement
UNION ALL
-- Recursive Select Statement that uses NameOfView
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how this compares to a non-view use of recursion in Teradata SQL, where a &lt;strong&gt;&lt;em&gt;recursive with&lt;/em&gt;&lt;/strong&gt; clause is followed by a final &lt;code&gt;SELECT&lt;/code&gt; that uses it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH RECURSIVE NameOfWith ( ListOfColumns) AS ( 
-- Seed Select Statement
UNION ALL
-- Recursive Select Statement that uses NameOfWith
)
SELECT
    Something
FROM
  NameOfWith
;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now look back to the &lt;code&gt;RECURSIVE VIEW&lt;/code&gt; syntactic structure and see that it does not have the final &lt;code&gt;SELECT&lt;/code&gt;. In effect, that happens when you use the view in a later select. Clearly the Teradata idea of a recursive view is only a way of saving the  &lt;code&gt;WITH RECURSIVE&lt;/code&gt; clause as a named item, available outside its own definition.&lt;/p&gt;

&lt;p&gt;By comparison, the SQLite idea seems to be that you're merely defining a view - hence &lt;code&gt;CREATE VIEW&lt;/code&gt; rather than &lt;code&gt;CREATE RECURSIVE VIEW&lt;/code&gt; but then allows you to put a recursive WITH inside the definition.&lt;/p&gt;

&lt;p&gt;Does this matter? Well it might.&lt;/p&gt;

&lt;p&gt;As it happened, the Teradata recursive view that I had written realy required being used with a WHERE clause each time - filtering &lt;code&gt;WHERE Up_Lvl_Cd IS NOT NULL&lt;/code&gt;&lt;br&gt;
In adding that final SELECT inside the view to make it work in SQLite, that WHERE clause was "burnt into" the view. But by the nature of that specific clause, having it also used in later uses of the view would be quite harmless.&lt;/p&gt;

&lt;p&gt;I suspect that converting in the other direction - from SQLite to Teradata - it would be prudent to add another view just to add the effect of that &lt;em&gt;final select&lt;/em&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Naming
&lt;/h4&gt;

&lt;p&gt;Another quirk of the object naming difference between the two environments, is that the Teradata syntax, has the strange thing by which the view may have had to be declared as being &lt;br&gt;
in a named database but then its non-database name must be used on the inside.&lt;/p&gt;

&lt;p&gt;To adapt the syntax example given above, we add &lt;code&gt;DatabaseName.&lt;/code&gt; to the create clause, but note that it cannot be used inside the definition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE RECURSIVE VIEW DatabaseName.NameOfView ( ListOfColumns) AS ( 
-- Seed Select Statement
UNION ALL
-- Recursive Select Statement that uses NameOfView
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That has led to my name changing scheme from earlier steps to have confused things - as only one of the two names was replaced - which led to some &lt;em&gt;fun&lt;/em&gt; until I twigged to what was going on.&lt;/p&gt;

&lt;p&gt;As already noted, that problem is not present in the SQLite syntax for which the named recursive element is an alias fully inside the view definition, rather than part of its &lt;em&gt;outside&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;To be honest, the process of conversion proved to be both as difficult as I expected (but with more details) all while clearly always going to be possible (thankfully because my knowledge proved to be sufficient to be confident that all the issues were covered). So it was both annoying and yet satisfying to achieve.&lt;/p&gt;

&lt;p&gt;If you've read this far I hope you found the annotations either helpful or interesting as an exercise. I wrote it because I have often been grateful for when other people openly documented their experiences.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>data</category>
    </item>
    <item>
      <title>Using SQL to discover problem dates in your data</title>
      <dc:creator>geraldew</dc:creator>
      <pubDate>Mon, 08 Apr 2024 14:54:38 +0000</pubDate>
      <link>https://forem.com/geraldew/using-sql-to-discover-problem-dates-in-your-data-4l8d</link>
      <guid>https://forem.com/geraldew/using-sql-to-discover-problem-dates-in-your-data-4l8d</guid>
      <description>&lt;h2&gt;
  
  
  The Issue
&lt;/h2&gt;

&lt;p&gt;When you work on a data warehouse, it's enough of an issue that there are probably very many tables to be using in your analysis.&lt;/p&gt;

&lt;p&gt;Lots of tables, means there are lots of columns, and most likely, a lot of those column will hold date data types. Anywhere there are dates there are potentials for having absurd values showing up in them.&lt;/p&gt;

&lt;p&gt;Where I work, a longstanding policy is to not allow vital date columns to support NULL values. Nevertheless, NULL values are need, often for an end-date field as a way to indicate that the record is still current. As a consequence, there is a practice of using the maximum possible date of 31st December 9999 for the pseudo-NULL value. Inevitably though, it seems that not all processes get written using the correct exact value, and so numerous instances of 1st January 9999 will show up as well as other misunderstandings of what should be used.&lt;/p&gt;

&lt;p&gt;While I have a fairly stock method for handling these things when I'm analysing end-date values - as per the &lt;code&gt;NULLIF&lt;/code&gt; expression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Cln_Dt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'31/12/9999'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FORMAT&lt;/span&gt; &lt;span class="s1"&gt;'dd/mm/yyyy'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;EnNulled_Cln_Dt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and which can be easily extend to handle a few such values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
        &lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
            &lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
                &lt;span class="n"&gt;Cln_Dt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'31/12/9999'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FORMAT&lt;/span&gt; &lt;span class="s1"&gt;'dd/mm/yyyy'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; 
                &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'01/01/9999'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FORMAT&lt;/span&gt; &lt;span class="s1"&gt;'dd/mm/yyyy'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; 
            &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'31/12/9000'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FORMAT&lt;/span&gt; &lt;span class="s1"&gt;'dd/mm/yyyy'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;EnNulled_Cln_Dt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but ultimately the problem becomes one of knowing quite which values to explicitly write the code to handle.&lt;/p&gt;

&lt;p&gt;Besides the specific date values, there is also the issue of knowing &lt;strong&gt;how often&lt;/strong&gt; this kind of thing is occurring.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Absent Solution
&lt;/h2&gt;

&lt;p&gt;Alas, the most likely thing you've thought as you're reading this is: surely any organisation with this kind of problem would acquire some kind of &lt;em&gt;Data Quality&lt;/em&gt; tool and tackle it head on?&lt;/p&gt;

&lt;p&gt;Yes, you are probably right to think that, and I'm certainly not going to argue against the idea. However I can honestly say that in over 20 years of doing data analysis work I've never known any actual progress to have been made on issues like this. Or maybe it has, but new processes for messing up data keep springing up.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;I could write a whole book on that topic, don't tempt me!&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So what I present here is something that can be done by anyone with the ability to write SQL and sufficient access permissions to create and populate a holding table. I don't claim it is super-clever - it is just something I wrote in a half afternoon and confirmed that it works.&lt;/p&gt;

&lt;p&gt;What you see is the SQL I wrote - slightly changed to not use names specific to my workplace - and then some annotations added to describe the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Method
&lt;/h2&gt;

&lt;p&gt;As I work on a Teradata, I'll be using its syntax and features, namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;making a couple of tables&lt;/li&gt;
&lt;li&gt;making a number of views&lt;/li&gt;
&lt;li&gt;making a number of macros&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, I will be using the way that Teradata provides a "data dictionary" - i.e. a way to query the system itself to find out what databases, tables and columns are on the system. In Teradata, this is done by a pseudo database called &lt;code&gt;DBC&lt;/code&gt; and various views within that. For our purpose here we'll just use one of those, one that lists all the columns, which is unsurprisingly called &lt;code&gt;DBC.ColumnsV&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;One other thing that I use for this, is that my SQL tool allows me to run an SQL statement - that has been written to itself generate yet more SQL as a set of return values - and then execute those return values. If you don't have that kind of feature in your tooling, that's ok - because you can usually manually clip the generated cover over from being a result to being submitted as more statements to execute.&lt;/p&gt;

&lt;p&gt;Corresponding to that, there are some issues about how to execute very large numbers of SQL statements and how to abandon them partway through. Those issues will not be covered in this article - but the method is structured to deal with the after effects of such interruptions.&lt;/p&gt;

&lt;p&gt;Similarly, in other system or dialects the implementation may need to use other-named features instead - e.g "stored procedures" and this article won't attempt to cover what those might be.&lt;/p&gt;

&lt;h2&gt;
  
  
  The SQL
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create Two Tables - Bank and Run
&lt;/h3&gt;

&lt;p&gt;I will want two tables to hold findings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one for use during a run&lt;/li&gt;
&lt;li&gt;and one for storing the latest findings across all runs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While I work on Teradata quite a lot, there can be slight variations among setups, so I have a stock method to use a "CREATE AS" to generate a prototype table. I then do a SHOW TABLE commend to see what the DDL looks like and then adapt it for the real table.&lt;/p&gt;

&lt;p&gt;Here's my prototype table maker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt;
  &lt;span class="n"&gt;TempDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_ExtremeDatesInColumns_Bank&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ColumnName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;DateValue&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;PresenceCount&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;LastCheckedAtDate&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;DBC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ColumnsV&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;
  &lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;
  &lt;span class="n"&gt;ColumnName&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt;
  &lt;span class="k"&gt;NO&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next is how wee see the DDL for the prototype table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt;
  &lt;span class="n"&gt;TempDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_ExtremeDatesInColumns_Bank&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I clip the output of that to a text editor and make some small changes.&lt;/p&gt;

&lt;p&gt;Next we drop the bank table - in case you're wondering, Teradata (still) does not provide a "drop if exists" statement in its SQL dialect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt;
  &lt;span class="n"&gt;TempDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_ExtremeDatesInColumns_Bank&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we make the real bank table - and is my customisation from the DDL of the prototype table. In this case I just added the UPI (unique primary index) which is the Teradata equivalent of a primary key setting. Another Teradata feature is &lt;code&gt;SET TABLE&lt;/code&gt; for which the other option is &lt;code&gt;MULTISET TABLE&lt;/code&gt; but I'm not going to explain the distinction here. I think "set tables" are a matter of good practice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; 
  &lt;span class="n"&gt;TempDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_ExtremeDatesInColumns_Bank&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;FALLBACK&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;NO&lt;/span&gt; &lt;span class="k"&gt;BEFORE&lt;/span&gt; &lt;span class="n"&gt;JOURNAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;NO&lt;/span&gt; &lt;span class="k"&gt;AFTER&lt;/span&gt; &lt;span class="n"&gt;JOURNAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;CHECKSUM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;MERGEBLOCKRATIO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;MAP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TD_MAP1&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;CHARACTER&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;UNICODE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="n"&gt;CASESPECIFIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;CHARACTER&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;UNICODE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="n"&gt;CASESPECIFIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ColumnName&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;CHARACTER&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;UNICODE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="n"&gt;CASESPECIFIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;DateValue&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="n"&gt;FORMAT&lt;/span&gt; &lt;span class="s1"&gt;'yyyy-mm-dd'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;PresenceCount&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;LastCheckedAtDate&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="n"&gt;FORMAT&lt;/span&gt; &lt;span class="s1"&gt;'yyyy-mm-dd'&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; 
  &lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ColumnName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;DateValue&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next I have a statement to drop the per-run table, which I would only need to do here if I was re-doing this sequence, because otherwise the table doesn't yet exist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt;
  &lt;span class="n"&gt;TempDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_ExtremeDatesInColumns_Run&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we make the per-run table - this is the same as for the bank table and Teradata lets us do a simple clone of the structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt;
  &lt;span class="n"&gt;TempDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_ExtremeDatesInColumns_Run&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
  &lt;span class="n"&gt;TempDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_ExtremeDatesInColumns_Bank&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt;
  &lt;span class="k"&gt;NO&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Define Views and Macros
&lt;/h3&gt;

&lt;p&gt;Next we define a View that exists just to give us a constant value of the number of days to wait before refreshing the check for extreme dates on a specific column. Teradata allows for a SELECT with no FROM clause, which makes this quite simple.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you're wondering why this is a View and not merely a matter of having the value quoted as a literal where it gets used, this method allows us to independently re-write this view to change the behaviour of our little system. While this is a small example, in other situations, this approach of putting definitions in vies can allow for quite sophisticated method variations.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_C_DaystoRefresh&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="mi"&gt;150&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;DaystoRefresh&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we define a View for how to get a list of date columns from the system. Here we are using knowledge about the DBC database on a Teradata system and its view holding metadata for the columns in every view and table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_C_DTC_DateColumns_Potential&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="k"&gt;TRIM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;C_V&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Dbn&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;TRIM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;C_V&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Tbn&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;TRIM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;C_V&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ColumnName&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Cln&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;DBC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ColumnsV&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;C_V&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="cm"&gt;/* -- later need to find a way to get the real data types for columns in views
  C_V.Type IN ( )
  AND
  */&lt;/span&gt;
  &lt;span class="n"&gt;C_V&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'SpecificDbs'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="cm"&gt;/* AND
  TableName IS NULL */&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;
  &lt;span class="n"&gt;C_V&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ColumnName&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%_Dt'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that I've left myself some code in a bracketed comment in case I wanted to filter the columns by actually having a DATE data type. In my case, the specific database I wanted to run analyses on was a space of only Views, and this resource does not hold their data types. Hence I've filtered them by assuming that their names would all end with "_Dt"&lt;/p&gt;

&lt;p&gt;Next we define a View for how to get a list of date columns to inspect this time around. While the base of this is the view we just defined for fetching the metadata, my data warehouse is so large that this would give me thousands of columns to analyse. While I do want to analyse them all, on any day when I run this, I won't want to re-analyse ones that I've done somewhat recently.&lt;/p&gt;

&lt;p&gt;To that end, I compare to the Bank table and look at the dates a column was last analysed. Similarly, if I had to interrupt &lt;em&gt;during&lt;/em&gt; a run then I don't want to re-analyse ones that I've just done, but which are pending my end-of-run step to merge into the Bank table.&lt;/p&gt;

&lt;p&gt;Both of those checks are simply LEFT OUTER JOINs and testing for a match through them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_C_DTC_DateColumns_BeyondDaystoRefresh&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;GetDateCols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dbn&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;GetDateCols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tbn&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;GetDateCols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cln&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Overdue_Holdings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Max_LastCheckedAtDate&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;DaysSinceLastCheck&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;CASE&lt;/span&gt; 
    &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;DaysSinceLastCheck&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'A'&lt;/span&gt;
    &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;'B'&lt;/span&gt; 
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;LastCheckCategory&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_C_DaystoRefresh&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;D_T_R&lt;/span&gt;
  &lt;span class="k"&gt;CROSS&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_C_DTC_DateColumns_Potential&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;GetDateCols&lt;/span&gt;
  &lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="c1"&gt;-- Overdue_Holdings&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
      &lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;ColumnName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;LastCheckedAtDate&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Max_LastCheckedAtDate&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt;
      &lt;span class="n"&gt;TempDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_ExtremeDatesInColumns_Bank&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
      &lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;ColumnName&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Overdue_Holdings&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt;
    &lt;span class="n"&gt;Overdue_Holdings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetDateCols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dbn&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt;
    &lt;span class="n"&gt;Overdue_Holdings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetDateCols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tbn&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt;
    &lt;span class="n"&gt;Overdue_Holdings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ColumnName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetDateCols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cln&lt;/span&gt;
  &lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="c1"&gt;-- During_Run = this is here to allow for the meta-SQL generation step to be stopped and re-started&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
      &lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;ColumnName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;LastCheckedAtDate&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Max_LastCheckedAtDate&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt;
      &lt;span class="n"&gt;TempDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_ExtremeDatesInColumns_Run&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
      &lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;ColumnName&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;During_Run&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt;
    &lt;span class="n"&gt;During_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetDateCols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dbn&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt;
    &lt;span class="n"&gt;During_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetDateCols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tbn&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt;
    &lt;span class="n"&gt;During_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ColumnName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetDateCols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cln&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;Overdue_Holdings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Max_LastCheckedAtDate&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
  &lt;span class="k"&gt;OR&lt;/span&gt; 
  &lt;span class="n"&gt;Overdue_Holdings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Max_LastCheckedAtDate&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;D_T_R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DaystoRefresh&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;
  &lt;span class="n"&gt;During_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Max_LastCheckedAtDate&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we define a View to provide a constant value for the number of years after the current year to treat as an extreme date. The setting here is quite arbitrary, but you should think carefully about setting it to zero or one - you might be surprised to find out how often your data includes "next year" or "next decade" values to be valid in various places.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_MetaSQL_V_YearsAfterCurrent&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="mi"&gt;150&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;YearsToSkip_Int&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;TRIM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;YearsToSkip_Int&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;YearsToSkip_Str&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we define the View that will be the SQL script builder for analysing the columns. I've been writing this kind of thing for many years. While it's always a bit messy doing this, hopefully you can read through the construction aspects and get a feel for what the SQL it generates will look like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_MetaSQL_V_ExtremeDatesInColumns&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; 
    &lt;span class="s1"&gt;'INSERT INTO '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; 
      &lt;span class="s1"&gt;'TempDbs.Prefix_DQ_ExtremeDatesInColumns_Run '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; 
      &lt;span class="s1"&gt;'( DatabaseName, TableName, ColumnName, DateValue, PresenceCount, LastCheckedAtDate ) '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="s1"&gt;'SELECT '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Dbn&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt; AS Db_N , '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Tbn&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt; AS Tb_N , '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Cln&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt; AS Cl_N , '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="n"&gt;Cln&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' AS DateValue , '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="s1"&gt;'COUNT( '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Cln&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;') AS PresenceCount , '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="s1"&gt;'CURRENT_DATE AS LastCheckedAtDate '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="s1"&gt;'FROM '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="n"&gt;Dbn&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'.'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Tbn&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="s1"&gt;'WHERE '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="s1"&gt;'EXTRACT( YEAR FROM '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Cln&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;') &amp;gt; EXTRACT( YEAR FROM CURRENT_DATE ) + '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Y_A_C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;YearsToSkip_Str&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="s1"&gt;'GROUP BY '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="n"&gt;Cln&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="s1"&gt;';'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;SqlStr&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;LastCheckCategory&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'#'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;DaysSinceLastCheck&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FORMAT&lt;/span&gt; &lt;span class="s1"&gt;'99999'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;CHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;':'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Dbn&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'.'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Tbn&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'.'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Cln&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Sorter&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_MetaSQL_V_YearsAfterCurrent&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Y_A_C&lt;/span&gt;
  &lt;span class="k"&gt;CROSS&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_C_DTC_DateColumns_BeyondDaystoRefresh&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;GetDateCols&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the &lt;code&gt;sorter&lt;/code&gt; column is both optional and arbitrary, the way I've constructed it here will let us prioritise columns that have not been analysed yet and then those done longest ago.&lt;/p&gt;

&lt;p&gt;Do note, that Teradata Views are not allowed to have an ORDER BY clause, so while we can provide the sorting column, actually applying it will have to wait for a Macro.&lt;/p&gt;

&lt;p&gt;Next we have (surprise!) a Teradata Macro, that really doesn't do any other than select just the generated SQL strings and in a prudent order.&lt;/p&gt;

&lt;p&gt;Also note that the generated SQL will all be INSERTs into the Run table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="n"&gt;MACRO&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_MetaSQL_M_ExtremeDatesInColumns&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;SqlStr&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_MetaSQL_V_ExtremeDatesInColumns&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
  &lt;span class="n"&gt;Sorter&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Being a SELECT, that macro will return rows when it gets run.&lt;/p&gt;

&lt;p&gt;Next we define a Macro for deleting all the rows in the Run table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="n"&gt;MACRO&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_R_ExtremeDatesInColumns_ClearRun&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;TempDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_ExtremeDatesInColumns_Run&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we define a Macro to perform the merge of the findings that were inserted into the Run table.&lt;/p&gt;

&lt;p&gt;To old people like me, this uses the "new" &lt;code&gt;MERGE INTO&lt;/code&gt; statement, but really it's been in Teradata for quite a long time now. For any Teradata users not familiar with it, you should note that the internal execution of this was written in a way that is much more efficient than either the older UPDATE or INSERT commands (but you'd need to read Teradata doco to see why that is). The syntax and run-time logic of this command can take some getting used to - but in this situation it fits the bill perfectly, and hopefully is fairly apparent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="n"&gt;MACRO&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_R_ExtremeDatesInColumns_MergeRun&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="n"&gt;MERGE&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt;
  &lt;span class="n"&gt;TempDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_ExtremeDatesInColumns_Bank&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;T_Bnk&lt;/span&gt;
&lt;span class="k"&gt;USING&lt;/span&gt;
  &lt;span class="n"&gt;TempDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_ExtremeDatesInColumns_Run&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;T_Run&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt;
    &lt;span class="n"&gt;T_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;T_Bnk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DatabaseName&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt;
    &lt;span class="n"&gt;T_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;T_Bnk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt;
    &lt;span class="n"&gt;T_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ColumnName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;T_Bnk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ColumnName&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt;
    &lt;span class="n"&gt;T_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;T_Bnk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateValue&lt;/span&gt;
&lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;MATCHED&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; 
  &lt;span class="n"&gt;PresenceCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;T_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PresenceCount&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;LastCheckedAtDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;T_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastCheckedAtDate&lt;/span&gt;
&lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="n"&gt;MATCHED&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt; 
  &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ColumnName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;DateValue&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PresenceCount&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LastCheckedAtDate&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;T_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;T_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;T_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ColumnName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;T_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateValue&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;T_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PresenceCount&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;T_Run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastCheckedAtDate&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Perform a Run
&lt;/h3&gt;

&lt;p&gt;Now, having created our tables, and defined all the views and macros, we can actually do a run.&lt;/p&gt;

&lt;p&gt;First Clear the Run table by running the macro for that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXECUTE&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_R_ExtremeDatesInColumns_ClearRun&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we run the macro that generates the SQL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXECUTE&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_MetaSQL_M_ExtremeDatesInColumns&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assuming you don't have a tool that can automate this, you can just copy all that output as text and re-paste it back into where you submit your SQL.&lt;br&gt;
Perhaps try just the first record on its own first.&lt;/p&gt;

&lt;p&gt;At this point, the design of our little system doesn't care how many of those rows you transplant over to being submitted. Perhaps keep doing a few until at least one indicates that it actually inserted something into the Run table.&lt;/p&gt;

&lt;p&gt;Next, after we've run as many of those as we feel like doing, we run the macro to merge the Run discoveries into the Bank table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXECUTE&lt;/span&gt;
  &lt;span class="n"&gt;myuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_R_ExtremeDatesInColumns_MergeRun&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With all that done, we inspect the results now in the Bank table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;TempDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prefix_DQ_ExtremeDatesInColumns_Bank&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
  &lt;span class="n"&gt;DatabaseName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ColumnName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;PresenceCount&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;DateValue&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Addendum
&lt;/h3&gt;

&lt;p&gt;As it happened, I wrote all the above and ran it and it chugged along nicely, taking different amounts of time for various tables and the date columns in them.&lt;/p&gt;

&lt;p&gt;However, when I inspected the results I realised it included something that I really wasn't interested in - in that it counted all the correct-dummy date of 31 December 9999.&lt;/p&gt;

&lt;p&gt;Because really, my interest was to show various people my findings of unusual values - for discussion about when to treat them as erroneous data and when to just treat them as alternate "NULL" end dates.&lt;/p&gt;

&lt;p&gt;The 31 December 9999 dates being in the results aren't themselves a problem, but if I want to avoid having them in there at all, then here is an extra couple of lines to insert to have it not bother collecting those values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;   &lt;span class="s1"&gt;'WHERE '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="s1"&gt;'NULLIF( '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Cln&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' , ( &lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt;31/12/9999&lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt; (DATE, FORMAT &lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt;dd/mm/yyyy&lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt;) ) ) IS NOT NULL '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="s1"&gt;'AND '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="s1"&gt;'EXTRACT( YEAR FROM '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Cln&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;') &amp;gt; EXTRACT( YEAR FROM CURRENT_DATE ) + '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Y_A_C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;YearsToSkip_Str&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;In a better world, problem dates would find enthusiastic people and tools for correcting errant values and managing data in organised ways. But while you wait for that to spontaneously happen (perhaps don't hold your breath) you can at least use your own skills to get a handle on the scale of the problem, with just plain old SQL.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>teradata</category>
    </item>
    <item>
      <title>License approval differences of the FSF and the OSI</title>
      <dc:creator>geraldew</dc:creator>
      <pubDate>Sun, 22 Oct 2023 13:13:00 +0000</pubDate>
      <link>https://forem.com/geraldew/license-approval-differences-of-the-fsf-and-the-osi-494c</link>
      <guid>https://forem.com/geraldew/license-approval-differences-of-the-fsf-and-the-osi-494c</guid>
      <description>&lt;p&gt;Are there actually significant distinctions between the Free Software Foundation (FSF) and the Open Source Initiative (OSI) in terms of the licenses they approve?&lt;/p&gt;

&lt;p&gt;As I’ve not seen anyone do a satisfactory page about this I felt it was time I tried to tackle the topic. At this point I won't claim it to be "comprehensive" but I hope at least it makes a good enough start.&lt;/p&gt;

&lt;p&gt;Much as I don't like when people post here on Dev with just a link to the whole article being somewhere else, I also don't want to maintain two copies while I might still be making minor corrections.&lt;/p&gt;

&lt;p&gt;So with that apology here is a link to the full article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://geraldew.wordpress.com/2023/10/22/how-many-and-of-what-nature-are-the-license-approval-differences-of-the-fsf-and-the-osi/"&gt;How many and of what nature are the license approval differences of the FSF and the OSI ?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are certainly other things to cover, presumably in a later posting. At the least, an updated and thorough listing from both organisations and cross-match would seem a next step. Also I suspect there are licenses that either organisation has judged, about which the other has given no public opinion.&lt;/p&gt;

&lt;p&gt;Oh, and to not tease, in short my finding was that: there are &lt;strong&gt;not&lt;/strong&gt; significant distinctions between the FSF and OSI approvals.&lt;/p&gt;

</description>
      <category>opensource</category>
    </item>
    <item>
      <title>Some Simple Scaling Progress Indication Log Functions</title>
      <dc:creator>geraldew</dc:creator>
      <pubDate>Mon, 07 Aug 2023 14:58:40 +0000</pubDate>
      <link>https://forem.com/geraldew/some-simple-scaling-progress-indication-log-functions-145p</link>
      <guid>https://forem.com/geraldew/some-simple-scaling-progress-indication-log-functions-145p</guid>
      <description>&lt;p&gt;I will prefix this piece by saying that this is just me sharing some ideas and code that I've used in a personal project. I am not claiming this is a unique creation nor that you or anyone else should take up using it.&lt;/p&gt;

&lt;p&gt;I recently saw a Mastodon post about a text function library for Python that did a progress bar in text mode displays. That reminded me of the progress logging method I devised and prompted me to consider writing about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;As I was writing my program "Foldatry" I felt a need to be able to see how it was going and where it was up to. For the most part, it works by crawling around folder structures, making records of what it finds and then comparing those findings in various ways.&lt;/p&gt;

&lt;p&gt;By its very nature, its job is to explore things not already known. As a consequence, a "progress bar" as normally conceived just isn't possible.&lt;/p&gt;

&lt;p&gt;In particular, my program is intended to operate on potentially very large complex directory structures, so I need something that would be workable regardless of the scale of files and folders it would be applied to.&lt;/p&gt;

&lt;p&gt;Foldatry is intended to be used either via its GUI or via the command line. In both modes, it shows what it is doing mainly by writing lines of text to logs. These logs can be shown in the GUI and/or written to text log files. Hence for showing progress, I wanted something that would write meaningful progress indicators but never blow out the size of the logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Concept
&lt;/h2&gt;

&lt;p&gt;When I think of handling things of unknown scale, my mind goes to logarithmic methods. In some of my professional data work I've made literal use of "log" functions (and in SQL at that) but for this I wanted something a bit clearer, and frankly, less "mathematical".&lt;/p&gt;

&lt;p&gt;Nonetheless, I knew I wanted something that changed what it did with scaled powers of ten. As a simple idea I wanted it to show the 1, 2, 3, 4 e4tc up to 10 and then 20, 30, 40 etc up to 100, then 200, 300 etc and so on. While that may seem quite a few lines of progress log, when applied to multi-multi-thousands it would soon settle to a reasonably finite amount.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;Of course, an idea is of no use unless we have code to implement it.&lt;/p&gt;

&lt;p&gt;Here's the initially code function.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I've not bothered to revisit it and make it more Pythonic - frankly it works and was only seen as a thing to quickly put in place during development.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oh - by the way - by accident I mistyped "progress" as "progess" when naming the first function and left it intact as it amused me and was a more unique search string than "progress" - my apologies for any discomfort that gives some readers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_n_to_show_progess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10000001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use this function, pass it a counter&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;thing&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the_things&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_n_to_show_progess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"Now at "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;  &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's not rocket science to work out what this will output, but let's make that explicit anyway.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Now at 1
Now at 2
Now at 3
Now at 4
Now at 5
Now at 6
Now at 7
Now at 8
Now at 9
Now at 10
Now at 20
Now at 30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;etc&lt;/p&gt;

&lt;p&gt;For one place where I was using this, I found that I was nearly always reaching to a count near or below 20, so I felt I may as well have a variant that I could tell to show me all the count points below some specified point. The implementation was trivial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_n_to_show_progess_showing_all_below_x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;is_n_to_show_progess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above functions all work fine if the circumstances are that every counter value is reached in sequence. But sometimes, we get to increment counters in jumps - for example a sub-call may bring back a variable size amount to add to the running count.&lt;/p&gt;

&lt;p&gt;For that case, we need a function that can use the same idea but be given a range of values to consider. Here is how that looks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_n_range_to_show_progess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;from_n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;inrange_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f_n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u_n&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;boo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;from_n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from_n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;boo&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;from_n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from_n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inrange_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inrange_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inrange_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inrange_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10000001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inrange_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upto_n&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the way to use it, is to keep a variable that holds what the count was before the possible jump and another for what it has become after the jump in value.&lt;/p&gt;

&lt;p&gt;Here is the place where it is used in Foldatry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;                &lt;span class="n"&gt;was_file_Count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;g_file_count&lt;/span&gt;
                &lt;span class="n"&gt;new_file_Count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;g_file_count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;found_n_files_deep_match&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;found_n_files_deep_mismatch&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;found_n_files_deep_errors&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cmntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_n_range_to_show_progess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;was_file_Count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_file_Count&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;p_multi_log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;do_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"dbs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Files:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;show_at_str&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later on, in using this method, I was becoming dissatisfied with the progress indications when the counts got up into high numbers. While the output was good for logs, that I would look at long after the whole run was finished, it was not very helpful as I was watching the program running live. I felt that what I wanted there was something that would show me progress at some general time interval rather than just those power-of-ten milestones. &lt;/p&gt;

&lt;p&gt;Here's what I came up with. Unlike the earlier versions, this now had a dependency, but I considered it enough of a stock module to depend on.&lt;/p&gt;

&lt;p&gt;Here's the code, then I'll describe what it does and how to use it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;im_time&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_n_to_show_progess_timed_get_mark&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;im_time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_n_to_show_progess_timed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;p_n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_time_diff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_time_diff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prev_time_mark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;i_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;im_time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;i_d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i_t&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;prev_time_mark&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i_d&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max_time_diff&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;r_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;i_d&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;min_time_diff&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p_n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;r_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;p_n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;r_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;p_n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;r_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;p_n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;r_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;p_n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;r_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;p_n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;r_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;p_n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10000001&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;r_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;r_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;r_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r_b&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;r_new_time_mark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;im_time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;r_new_time_mark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prev_time_mark&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r_new_time_mark&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_n_to_show_progess_timed_stock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;p_n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_time_mark&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;min_time_diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="c1"&gt;# so every is_n will be reported
&lt;/span&gt;    &lt;span class="n"&gt;max_time_diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="c1"&gt;# show something every 15 seconds
&lt;/span&gt;    &lt;span class="n"&gt;r_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r_new_time_mark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;is_n_to_show_progess_timed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;p_n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_time_diff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_time_diff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_time_mark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r_new_time_mark&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, in practice, I am using the latter call - &lt;code&gt;is_n_to_show_progess_timed_stock&lt;/code&gt; which merely bakes in, a minimum time of 5 seconds and a maximum of 15 seconds - so we'll presume that for describing. Just note that the middle call can always be used for any preferred number of seconds.&lt;/p&gt;

&lt;p&gt;Here is how we use it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First we get a time mark value. We just do this so we can pass it in the next call.&lt;/li&gt;
&lt;li&gt;Then, we call our progress function, giving it our counter and the timer mark - we get back a boolean, and a timer value to replace the one we passed
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;i_time_mark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;is_n_to_show_progess_timed_get_mark&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# do things in some kind of loop
&lt;/span&gt;    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i_time_mark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;is_n_to_show_progess_timed_stock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i_time_mark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"show progress "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an example of the log entries written with this method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;File: 2023-08-06 11:29:09,447 - Matchsubtry traverse_tree_recurse: File count = 2
File: 2023-08-06 11:29:21,900 - Matchsubtry traverse_tree_recurse: File count = 3
File: 2023-08-06 11:29:57,633 - Matchsubtry traverse_tree_recurse: File count = 5
File: 2023-08-06 11:30:30,194 - Matchsubtry traverse_tree_recurse: File count = 8
File: 2023-08-06 11:30:35,686 - Matchsubtry traverse_tree_recurse: File count = 9
File: 2023-08-06 11:31:07,213 - Matchsubtry traverse_tree_recurse: File count = 11
File: 2023-08-06 11:31:48,356 - Matchsubtry traverse_tree_recurse: File count = 14
File: 2023-08-06 11:32:33,264 - Matchsubtry traverse_tree_recurse: File count = 19
File: 2023-08-06 11:32:39,236 - Matchsubtry traverse_tree_recurse: File count = 20
File: 2023-08-06 11:33:07,987 - Matchsubtry traverse_tree_recurse: File count = 22
File: 2023-08-06 11:33:46,181 - Matchsubtry traverse_tree_recurse: File count = 25
File: 2023-08-06 11:34:25,549 - Matchsubtry traverse_tree_recurse: File count = 28
File: 2023-08-06 11:35:00,026 - Matchsubtry traverse_tree_recurse: File count = 30
File: 2023-08-06 11:35:35,388 - Matchsubtry traverse_tree_recurse: File count = 33
File: 2023-08-06 11:35:55,221 - Matchsubtry traverse_tree_recurse: File count = 36

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may notice several things going on there:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;not all the values between 1 and 10 were written - that's because some of those happened faster than the minimum specified time&lt;/li&gt;
&lt;li&gt;values in between the multiples of 10 are showing up - that's because the maximum specified time had passed&lt;/li&gt;
&lt;li&gt;values of the multiples of 10 still showed up - while these wouldn't be guaranteed to, these probably all will for some scaling degree or other.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;These functions are a simple idea that I've found useful. I suspect I may yet think of some other variations on this idea, but will just let ongoing events prompt me to do so.&lt;/p&gt;

</description>
      <category>python</category>
    </item>
    <item>
      <title>Coding Diary 2023-03-19 an undesired feature in filecmp.py</title>
      <dc:creator>geraldew</dc:creator>
      <pubDate>Sun, 19 Mar 2023 13:26:53 +0000</pubDate>
      <link>https://forem.com/geraldew/coding-diary-2023-03-19-an-undesired-feature-in-filecmppy-3hjj</link>
      <guid>https://forem.com/geraldew/coding-diary-2023-03-19-an-undesired-feature-in-filecmppy-3hjj</guid>
      <description>&lt;p&gt;I'm writing this just an exercise in open sharing. Perhaps it can show that there's value in copying a stock Python module and playing around with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;My program Foldatry is intended to run unattended for long periods of time as it pores over gigabytes and terabytes of files and folders. Here the focus is on a section that seeks to prove that two structures of files are identical - a useful thing to do after a big copy operation.&lt;/p&gt;

&lt;p&gt;The function for doing this was meant to do a two part run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first a "light touch" in which only the metadata was compared;&lt;/li&gt;
&lt;li&gt;then if that found all looked the same, then a second "heavy touch" run would compare all the file contents as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But was happening was that the amount of time being spent in the first round was taking much longer than expected. Indeed it &lt;em&gt;seemed&lt;/em&gt; to be taking as long as I would expect the heavy touch to run.&lt;/p&gt;

&lt;p&gt;So what was going on?&lt;/p&gt;

&lt;p&gt;As it happened, I've long had good enough logging built into the program to show me where the problem was. It seemed to be inside my use of the &lt;code&gt;dircmp&lt;/code&gt; class in the stock Python module &lt;code&gt;filecmp.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the documentation link for that module: &lt;a href="https://docs.python.org/3.11/library/filecmp.html"&gt;https://docs.python.org/3.11/library/filecmp.html&lt;/a&gt; and from it, here is the brief for that class/object:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The dircmp class&lt;/p&gt;

&lt;p&gt;class filecmp.dircmp(a, b, ignore=None, hide=None)&lt;/p&gt;

&lt;p&gt;Construct a new directory comparison object, to compare the directories a and b. ignore is a list of names to ignore, and defaults to filecmp.DEFAULT_IGNORES. hide is a list of names to hide, and defaults to [os.curdir, os.pardir].&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As it happens, I had looked at this module a while back (&lt;em&gt;and that's another story&lt;/em&gt;) so I did have some idea of how it works. Enough to know that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it is written purely in Python - although itself using some other stock Python modules&lt;/li&gt;
&lt;li&gt;therefore, I could copy the module code into my own program and modify it as a way to see what was happening.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So that's what I did.&lt;/p&gt;

&lt;p&gt;Being a little careful, I fetched a copy from the version of Python that I'm using (on Xubuntu 20.04) - from &lt;a href="https://github.com/python/cpython/blob/3.7/Lib/filecmp.py"&gt;https://github.com/python/cpython/blob/3.7/Lib/filecmp.py&lt;/a&gt; and put it, slightly renamed, into my source code folder. As I'd already been using this module via an alias on import, I only had to change the reference to the now-local renamed copy.&lt;/p&gt;

&lt;p&gt;After a quick check that this had taken effect, I started inserting &lt;code&gt;print&lt;/code&gt; statements to see which internal functions were being called.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Issue Identified
&lt;/h2&gt;

&lt;p&gt;Note: to keep this from being tedious I'll skip some of the concepts of this module and jump to the particular.&lt;/p&gt;

&lt;p&gt;I soon determined that the flaw lay in a section where the metadata of files was being compared.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_sig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;f1&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_sig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;f2&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;S_IFREG&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;S_IFREG&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;shallow&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the context of use by the &lt;code&gt;dircmp&lt;/code&gt; class, the parameter &lt;code&gt;shallow&lt;/code&gt; will be in a state of &lt;code&gt;True&lt;/code&gt; and this where things were failing. And by failing, the internal file comparison function was thereby continuing to compare the files byte-by-byte - which is why it was being &lt;em&gt;heavy&lt;/em&gt; instead of being &lt;em&gt;light&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Ok, so that begged the question of why that was failing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Values made &lt;code&gt;.sig&lt;/code&gt; call
&lt;/h3&gt;

&lt;p&gt;If you look at the code section above, you'll see that the &lt;code&gt;s1 == s2&lt;/code&gt; comparison is on values that had been fetched by calling a function &lt;code&gt;_sig&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the code for that function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_sig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;S_IFMT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_mode&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_mtime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's nothing particularly obviously wrong with that, though the detail will be in the feature being used from the stock module &lt;code&gt;stat&lt;/code&gt;  but happily I didn't need to go inspect that module. Instead I just added some &lt;code&gt;print&lt;/code&gt; statements to inspect the values that were going into the &lt;code&gt;return&lt;/code&gt; clause. Here they are:&lt;/p&gt;

&lt;p&gt;For file A:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;32768&lt;/li&gt;
&lt;li&gt;11&lt;/li&gt;
&lt;li&gt;1673954681.5983226&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For file B:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;32768&lt;/li&gt;
&lt;li&gt;11&lt;/li&gt;
&lt;li&gt;1673954681.0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clearly the difference is in the third value, which was being fed from &lt;code&gt;st.st_mtime&lt;/code&gt; and while you can look that up, I can tell you it's just a &lt;code&gt;float&lt;/code&gt; number representing the modification timestamp of the files. Being a float, the integer portion is the POSIX time in seconds. The fractional portion of the number is therefore the fractions of a second for the timestamp. As you can see, for one of the files the timestamp was stored with a fractional component and the other without one.&lt;/p&gt;

&lt;p&gt;While the question of whether timestamps are a good enough thing to be comparing - in deciding generally whether the file contents should be compared - is clearly a good question, in this context it is being so picky about the fractional parts that's causing my problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;So, in the short term, a simple &lt;strong&gt;fix&lt;/strong&gt; is for me to  just add an &lt;code&gt;int&lt;/code&gt; function on the times being collected so that only their integer components will be compared.&lt;/p&gt;

&lt;p&gt;Thus, the very minor code change is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_sig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;S_IFMT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_mode&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_mtime&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, having a local copy of a stock Python module is not a good situation, but there is a wider arc to that story. &lt;/p&gt;

&lt;h2&gt;
  
  
  Addendum 1
&lt;/h2&gt;

&lt;p&gt;As it happens, I had already realised some time ago that I should write my own replacement for the &lt;code&gt;filecmp.py&lt;/code&gt; module - not so much because I think it's bad, but because my needs are both more specific than it is intended to handle as well as needing something more flexible.&lt;/p&gt;

&lt;p&gt;Indeed the very issue that prompted this post is an example - that there could be various strategies about matching files - their names and/or their metadata and/or their content -that I'd like handled in more controllable ways.&lt;/p&gt;

&lt;p&gt;At the moment, some parts of those ideas are implemented as things to check and/or do after using the &lt;code&gt;dircmp&lt;/code&gt; class. While I did start writing the replacement module - it's in the code base but is unused - I don't intend tackling it until I have settled all the different things I want it to cover. These are described in part of the Foldatry documentation so I won't detail them here.&lt;/p&gt;

&lt;p&gt;However, for the context of this article, I will say that being able to usefully compare files that have been copied from one file system to another is a definite need that I have - and this is likely to throw up differences of how the timestamps are stored and handled on them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; there are some useful comments about this at &lt;a href="https://note.nkmk.me/en/python-os-stat-file-timestamp/"&gt;Get file timestamps (creation, modification, access date and time) in Python&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Addendum 2
&lt;/h2&gt;

&lt;p&gt;On how the stock module "works".&lt;/p&gt;

&lt;p&gt;When I needed to look at this a while back it took me some reading and thinking to see why it was written the way it is. As an overview. it tries to use a "phased" approach to doing the comparisons - thus allowing a fairly simple external call to lead to quite varied amounts of operation depending on what is encountered in the folders and files.&lt;/p&gt;

&lt;p&gt;When I realised that, I could see the sense of how it was written. It happens that for my needs in Foldatry, I - the application programmer - want to be more in control than that - so that told me I will need to write something more suited.&lt;/p&gt;

&lt;p&gt;I do happen to think that the module is written a bit more cryptically than it needs to be - but that's not an uncommon opinion I have of Python that I see written.&lt;/p&gt;

&lt;h2&gt;
  
  
  Addendum 3
&lt;/h2&gt;

&lt;p&gt;There's an unimportant story of why it took me so long to notice this undesired feature - and do note that I'm not calling this a "bug".&lt;/p&gt;

&lt;p&gt;When I started Foldatry, the confirmation of perfect copies was the first part that I wrote - before any GUI and even before the primary reason I wrote anything (which was the matchsubtry feature).&lt;/p&gt;

&lt;p&gt;That means that my testing was being done on large amounts of files from very early on. However, it was only quite a while later than I hit on the idea of doing a "light touch" run before embarking on the full file contents comparison. When I tested this, I probably didn't run it on very large file sizes and so saw no significant delays. And when I have run it - for real usage - on very large structures, knowing such a run would take hours, I'd run it overnight - so again, not noticing that the light touch run wasn't actually fast. I would have seen confirmation of the two runs in the logs but not inspected the times closely.&lt;/p&gt;

&lt;p&gt;Of course, for the light touch to take a long time was dependent on there being minor differences in the timestamps - and in retrospect I'm guessing that may depend on which copying tools I had used and/or the sequences of copies and moves between ext4 and NTFS file systems (as most of my USB drives use the latter).&lt;/p&gt;

&lt;p&gt;Oh well. I got there eventually. And I will at some point write the replacement module.&lt;/p&gt;

</description>
      <category>python</category>
    </item>
    <item>
      <title>Musing about Programming - Abstraction vs Specifics</title>
      <dc:creator>geraldew</dc:creator>
      <pubDate>Fri, 30 Dec 2022 11:35:41 +0000</pubDate>
      <link>https://forem.com/geraldew/musing-about-programming-abstraction-vs-specifics-181b</link>
      <guid>https://forem.com/geraldew/musing-about-programming-abstraction-vs-specifics-181b</guid>
      <description>&lt;p&gt;This article is merely musing, I can't really advise that it will be worth your while to read it. &lt;/p&gt;

&lt;p&gt;Sometimes I think that the major unsolved (untackled?) problem in programming is the matter of separating the abstracted concept of a program from the specifics of how to do it in the given language of the initial implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;Take my program Foldatry for example. I wrote it because I had a particular kind of file and folder operation in mind and no program had seemed to tackle it.&lt;/p&gt;

&lt;p&gt;But, in order to realise my concept - literally meaning to make it real - I needed to try creating it in some programming language. As I had been long intending to learn Python and because it seemed to provide the features I would need, I chose Python.&lt;/p&gt;

&lt;p&gt;To be clear, at the point of setting out to write it, I was quite confidant that what I had in mind could be done. My concept seemed inherently logical and so should be programmable. But, I knew I was going to have to "work out" the actual logic as I progressed through the coding. Hence, while I had a hunch, I did not know how much code it was going to require.&lt;/p&gt;

&lt;p&gt;For Foldatry. the core feature I'm referring to here, is the part that became named as "matchsubtry" (match sub tree). I've described the concept in more detail at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/geraldew/foldatry/-/blob/main/docs/matchsubtry.md"&gt;matchsubtry.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After writing it, thus proving both that the concept was sound and having an actual example implementation, I got on with writing other parts of the general tool that is Foldatry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whither Abstraction
&lt;/h2&gt;

&lt;p&gt;But while I began with an abstract concept, now that I've made that concept real, writing out a just-sufficiently-detailed description of it feels like an exercise of documentation rather than of programming. This seems to beg a question of why I wasn't able to create the basis of the program in some abstract form. &lt;/p&gt;

&lt;p&gt;This process of abstraction and re-implementation is not a great mystery to programmers, it has been done many times when "porting" a program from one language to another.&lt;/p&gt;

&lt;p&gt;Note that I'm considering this kind of re-implementation to only be where the methods used in the program are understood. There are contrasting examples where people have looked at the running behaviour of a program and then independently written something to emulate that behaviour. Of course, sometimes that behaviour implies so obvious an implementation method that the line between these tropes is blurred.&lt;/p&gt;

&lt;p&gt;But, as I've noted elsewhere, I didn't (and still don't) think Python is a suitable environment in which to write a reliable and performant program. Thus I consider Foldatry to merely be a working prototype for other people to take its ideas and implement them in a better language (e.g. Rust).&lt;/p&gt;

&lt;p&gt;Ah, but is there a catch? That perhaps I have inadvertently buried the port-able concepts in among the idiosyncrasies of Python?&lt;/p&gt;

&lt;p&gt;What if there was a way of writing some meta-form of Foldatry that expresses the cross-language concepts of the program? At a first level, such a meta-program could be used by other  people to guide them in implementing an adaptation of Foldatry into some other language and/or environment. At a second level, could such a meta-language be usable to generate quite a lot of the other language program?&lt;/p&gt;

&lt;p&gt;Now, of course, I'm not naive enough to imagine I'm the first to think along these lines. Indeed I'm sure I sketched thoughts like this back in the 1990s or even the 1980s. I have deliberately chosen to write this article &lt;em&gt;before&lt;/em&gt; I go looking online to see what ever has been worked out along the arc.&lt;/p&gt;

&lt;p&gt;I am aware that there are many web sites and/or service companies that offer to automate some parts of the language conversion process. I do not have the impression that any of those work so well as to cause programmers in general to see them as obvious paths to take.&lt;/p&gt;

&lt;h2&gt;
  
  
  Another example - Tk
&lt;/h2&gt;

&lt;p&gt;Part of the prompt to write this piece was a morning's coffee musing about my use of the Tk GUI library (via the Python Tkinter module) in Foldatry and the extent to which that also traps the program into Python. There seems to be a static subset of languages that have well established usage of Tk - and that still leaves out most compiler based languages.&lt;/p&gt;

&lt;p&gt;Indeed, the way that all the supported uses of Tk seem to exist is by linking to the dynamic Tcl and Tk libraries as written in C. This helps explain why there is no such thing as Tk support for JavaScript or Go (as in golang).&lt;/p&gt;

&lt;p&gt;The reason I bring up Tk in this context is that my impression of it, is precisely that its use sits inside a Python program as a form of minimal concept expression. Tk uses its own abstractions such as Frames and allows the coder to only specify the things that are crucial to how the GUI will operate (well, operation and layout, really).&lt;/p&gt;

&lt;p&gt;As I was writing the GUI section of Foldatry I found that there was a nice fit between me trying to find the logic of how various parts of my program's interface should relate, and how Tkinter required me to specify things. As a consequence I'm hoping that were I to re-implement it - in another language and another GUI toolkit, that I would be tasked only with how to reinstate the concepts in the new environment - and not be unpicking parts of how Tkinter works, (But that, of course it merely a guess.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstraction and Re-implementation
&lt;/h2&gt;

&lt;p&gt;While my title for this article is - &lt;strong&gt;Abstraction vs Specifics&lt;/strong&gt; - it is perhaps better given as "&lt;em&gt;Abstraction and Re-implementation&lt;/em&gt;" because that is the task that often confronts us in development. While I've given the example of translation from one programming language to another, it might just as well have been about keeping it in the same language and trying for a fresh implementation. Especially as that perhaps happens just as often. Yes, this is where we insert a certain megacorp's numerous claims to have "rewritten from scratch" their products - a claim that has proven to be false for every instance I've ever inspected.&lt;/p&gt;

&lt;p&gt;We've been at this programming thing for quite a while, and yet we still don't have any "standard" (meaning: in common use) pseudo-code language. We programmers merely take that as read, but perhaps we should occasionally ask ourselves why that is.&lt;/p&gt;

&lt;p&gt;I have seen people inadvertently suggest that Python might itself be such a language. Sadly that suggestion is farcical but I can see the good thought that prompts it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To avoid getting too catty about Python, let's recognise that Python was intended as a multi-paradigm scripting language, which is enough to validate the suggestion. Perhaps if it had stayed simple then it might have served as one, but the numerous infiltrations from the C-Python implementation into the definition of the language surely stopped Python being a &lt;em&gt;simple and clear&lt;/em&gt; language many, many years ago now. Much of what is written in contemporary Python now is essentially cryptic puzzles to 90% of programmers (yes, including those who code only in Python).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm not trying to suggest that the needed panacea is any particular pseudo-code definition, rather I'm pondering what the good-enough set of programming concepts would be, so as to support one or several pseudo-code regimes.&lt;/p&gt;

&lt;p&gt;This might involve some heavy thinking about which paradigms are vital and which merely convenient. There are plenty of web pages out there stating the important paradigms so I won't try to list them comprehensively here. But I also don't want to play hard to get, so obviously we are talking here about such things as: expressions, iteration, recursion, procedural, object-oriented, functional etc. The wars about which of those are ideal for workaday, practical, commercial, scalable (the adjective list here is probably endless) programming can continue unaffected. I am only interested in what would make for something good enough to act as a broad way to write out the vital concepts of a given program idea.&lt;/p&gt;

&lt;p&gt;Or perhaps, we haven't achieved such a thing because it isn't possible. As in fundamentally, inherently impossible.&lt;/p&gt;

&lt;p&gt;Or perhaps we haven't achieved it because it is our behaviour to not come together well enough to make one work.&lt;/p&gt;

</description>
      <category>python</category>
      <category>tkinter</category>
      <category>programming</category>
      <category>philosophy</category>
    </item>
    <item>
      <title>Python Tkinter - An Exercise in Wrapping the ComboBox</title>
      <dc:creator>geraldew</dc:creator>
      <pubDate>Thu, 15 Dec 2022 12:57:47 +0000</pubDate>
      <link>https://forem.com/geraldew/python-tkinter-an-exercise-in-wrapping-the-combobox-ndb</link>
      <guid>https://forem.com/geraldew/python-tkinter-an-exercise-in-wrapping-the-combobox-ndb</guid>
      <description>&lt;p&gt;I recently solved a small problem by wrapping a sub-class around theTkinter ComboBox definition. Having made notes as I was doing so, I figured I may as well tidy and share what I did.&lt;/p&gt;

&lt;p&gt;There are plenty of tutorials around about doing this kind of thing - I'm not claiming this to be anything special or better.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;It so happens that the environment in which I have done most of my application coding over the years is one that provides a "ComboBox" control aka a drop-down list.&lt;/p&gt;

&lt;p&gt;But in that environment I am very used to having the ComboBox fed by a small table, usually as rows with key and show columns. The control is bound to the key but displays the show columns - this being achieved by setting the dispay width of the first column to zero. Thus the Combobox has values just for displays but when I read what the user selected, I get the key. Most of the time I use numeric keys and string displays. When supplying these via a table, an order can be specified - that might be independent of the display values.  &lt;/p&gt;

&lt;p&gt;Having come to writing applications in Python and using Tkinter, it has been frustrating that this only supports the idea of the displayed values being exactly what the control returns as the user's selection.&lt;/p&gt;

&lt;p&gt;While annoying, this issue just hadn't been important enough to go out of my way to solve. For some reason, I suddenly felt like tackling - hence this annotated journey.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Note: I am deliberately overlooking that we can fetch the &lt;em&gt;index&lt;/em&gt; of the selected item, rather than the displayed value. Most of the time, I don't really care about where things are in the displayed drop-down list - and want to be free to re-order them, or leave an item out. However, as we'll later on, the index might be a useful once we've worked how to wrap one class inside another. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Enter Enumerations
&lt;/h2&gt;

&lt;p&gt;As an old Pascal programmer, I quite like using defined types and enumerations in my programming. The most important reason for liking them - especially as a combination - is that a compile phase will pick up any attempt to stray from assigning across the type definitions.&lt;/p&gt;

&lt;p&gt;However, Python as an interpreted language with dynamic typing doesn't really provide that benefit. Nonetheless I still quite like using enumerations as it lets me write symbolic values in the program code and ignore the specific values much of the time. &lt;/p&gt;

&lt;p&gt;To quote from &lt;a href="https://en.wikibooks.org/wiki/Pascal_Programming/Enumerations"&gt;WikiBooks: Pascal Programming/Enumerations&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight pascal"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;
    &lt;span class="n"&gt;weekday&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Monday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Tuesday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Wednesday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Thursday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Friday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Saturday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sunday&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt;
    &lt;span class="n"&gt;startOfWeek&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;weekday&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;begin&lt;/span&gt;
&lt;span class="n"&gt;startOfWeek&lt;/span&gt; &lt;span class="p"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Sunday&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For internal use for a compiled program, the actual (binary) values that will be used for the enumeration values simply don't matter. And being a strongly typed language, the Pascal compiler will give an error for any attempt to assign a non "weekday" value to the variable "startOfWeek".&lt;/p&gt;

&lt;p&gt;Of course, Python is quite different, and I won't explain that in detail - see the standard Python documentation for &lt;a href="https://docs.python.org/3/library/enum.html"&gt;enum — Support for enumerations&lt;/a&gt; - but suffice to say I find them useful enough to use them.&lt;/p&gt;

&lt;p&gt;However this does pose a problem with comboboxes - because they need to be fed with strings to display and therefore they return those strings back as the selected value.&lt;/p&gt;

&lt;p&gt;To get around this, I have been creating my enumerations, and then adding to them by writing functions to express each enumerated value as a display/select string and a converse function to get the enumeration that has a specific string. It all works, but seems clumsy.&lt;/p&gt;

&lt;p&gt;What I would like is to have Comboboxes to which I can pass a list of pairs, such that the string halves get displayed to the user but I can get back the enumeration half (key) for that string.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Approach
&lt;/h2&gt;

&lt;p&gt;Currently, what I do is to have a Tk variable that is bound to the &lt;code&gt;textvariable&lt;/code&gt; property of the &lt;code&gt;Combobox&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Hence the setup portion of the code does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create Tk variable&lt;/li&gt;
&lt;li&gt;create Tk Combobox control with binding to the variable&lt;/li&gt;
&lt;li&gt;set the &lt;code&gt;values&lt;/code&gt; for the Combobox control&lt;/li&gt;
&lt;li&gt;set the default value for the Combobox by setting the bound variable
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;t06_t2_str_scheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m_tkntr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringVar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
    &lt;span class="n"&gt;t06_t2_cb_schemes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m_tkntr_ttk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Combobox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;t06_t2_frame02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;textvariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t06_t2_str_scheme&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;t06_t2_cb_schemes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'values'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MatchScheme_GetTuple&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;t06_t2_str_scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;cmntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MatchScheme_Default_Value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In which I'm pulling from some support functions that I define along with my enumerations - namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MatchScheme_GetTuple()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MatchScheme_Default_Value()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, to later read the selection, we just &lt;code&gt;get&lt;/code&gt; the value of the bound variable&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;got_selection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t06_t2_str_scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And for which I will then need to call another function to translate from the selected string to the corresponding enumeration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;enum_selection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MatchScheme_EnumOfString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;got_selection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's not that doing this is hard, it's just fiddly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Want To Do
&lt;/h2&gt;

&lt;p&gt;I want to be able to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;have a list of tuples as what I set for the Combobox&lt;/li&gt;
&lt;li&gt;in which the first value in each tuple is the value I want to get back&lt;/li&gt;
&lt;li&gt;the second value in each tuple is the value I want displayed for selection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;e.g. define the list of tuples&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;the_ListTuplePair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"One"&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Two"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Three"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Four"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;e.g. create the Combobox object&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;t06_t2_cb_schemes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TuplePairCombobox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;t06_t2_frame02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;listTuplePair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;the_ListTuplePair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;defaultKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;e.g. the collection of the selection&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;the_selection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t06_t2_cb_schemes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getSelectedKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Caveats and Cases
&lt;/h2&gt;

&lt;p&gt;While mulling over some of the potential issues, I considered I would need to deal with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;that no repeats of the keys should get processed and displayed, and hence not selected&lt;/li&gt;
&lt;li&gt;that repeats of the display values &lt;strong&gt;is&lt;/strong&gt; allowed and catered for&lt;/li&gt;
&lt;li&gt;a default key that matches no provided key means no default value is set&lt;/li&gt;
&lt;li&gt;that no selection being made returns as None&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation A - using the value bound to the control
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Air Code
&lt;/h3&gt;

&lt;p&gt;Reminding myself a little bit about how class definitions work in Python (I do tend to avoid them) I sat down and typed up the following (with a fair bit of toying around):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TuplePairCombobox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;m_tkntr_ttk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Combobox&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_process_listTuplePair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;ip_listTuplePair&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# ensure the listTuplePair has no repeats of the keys
&lt;/span&gt;        &lt;span class="n"&gt;t_list_keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="c1"&gt;# used to detect repeats
&lt;/span&gt;        &lt;span class="n"&gt;r_dict_valu2key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c1"&gt;# use both to populate the combobox and reverse lookup after selection
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ip_listTuplePair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;t_list_keys&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;pass&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;i_dict_valu2key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t_list_keys&lt;/span&gt; &lt;span class="c1"&gt;# number of space to pad the display string
&lt;/span&gt;                    &lt;span class="n"&gt;i_dict_valu2key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;
                    &lt;span class="n"&gt;i_dict_valu2key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;t_list_keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r_dict_valu2key&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_listTuplePair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_defaultKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_StringVar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m_tkntr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringVar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="n"&gt;i_dict_valu2key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_process_listTuplePair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;p_listTuplePair&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;textvariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_StringVar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'values'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;i_dict_valu2key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p_defaultKey&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;i_dict_valu2key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_StringVar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_defaultKey&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getSelectedKey&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;txt_selection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_StringVar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;r_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i_dict_tpls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;txt_selection&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r_key&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That seemed to cover the most of how the idea would work. A short description might be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;process the supplied list of tuples to build a dictionary that will act as both the reverse lookup - to go from the selected display to the tuple keys - and as the list of things to display in the combobox&lt;/li&gt;
&lt;li&gt;apply my logical rules about repeats, of keys and of values&lt;/li&gt;
&lt;li&gt;to deal with any repeated values, look for clashes and add extra spaces to their ends, with different numbers of spaces as we go along &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Actual Code
&lt;/h3&gt;

&lt;p&gt;Here is what I had after pasting in the air code and fixing some of its oversights. &lt;/p&gt;

&lt;h4&gt;
  
  
  The Class
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TuplePairCombobox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;m_tkntr_ttk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Combobox&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_process_listTuplePair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip_listTuplePair&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# ensure the listTuplePair has no repeats of the keys
&lt;/span&gt;        &lt;span class="n"&gt;t_list_keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="c1"&gt;# used to detect repeats
&lt;/span&gt;        &lt;span class="n"&gt;r_dict_valu2key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c1"&gt;# use both to populate the combobox and reverse lookup after selection
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ip_listTuplePair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;t_list_keys&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;pass&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;r_dict_valu2key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;t_list_keys&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# number of space to pad the display string
&lt;/span&gt;                    &lt;span class="n"&gt;r_dict_valu2key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;r_dict_valu2key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;t_list_keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r_dict_valu2key&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_listTuplePair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_defaultKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_StringVar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m_tkntr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringVar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_dict_valu2key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_process_listTuplePair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;p_listTuplePair&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;textvariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_StringVar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'values'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_dict_valu2key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p_defaultKey&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_dict_valu2key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_StringVar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_defaultKey&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getSelectedKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;txt_selection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_StringVar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;txt_selection&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_dict_valu2key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;r_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_dict_valu2key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;txt_selection&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; 
            &lt;span class="n"&gt;r_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r_key&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The Object
&lt;/h4&gt;

&lt;p&gt;Here is how the new feature gets used.&lt;/p&gt;

&lt;p&gt;First the creation of the ComboBox object, with the usual Tkinter pair of: create and place steps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;i_defaultKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="n"&gt;t02_cb_schemer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TuplePairCombobox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;t02_frame50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i_listTuplePair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i_defaultKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="n"&gt;t02_cb_schemer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m_tkntr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LEFT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;padx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tk_padx&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;pady&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tk_pady&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The Value Fetch
&lt;/h4&gt;

&lt;p&gt;Here is how the value is collected from the ComboBox.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;t02_cb_schemer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getSelectedKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The List of Tuples
&lt;/h4&gt;

&lt;p&gt;Here is a mock example of a list of tuples for supplying to the new &lt;code&gt;TuplePairCombobox&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="c1"&gt;#i_listTuplePair = [ ( 1, "One" ), ( 2, "Two"), ( 3, "Three"), ( 4, "Four") ]
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The List of Tuples with a Repeated Value
&lt;/h4&gt;

&lt;p&gt;Just to check that my extra edge case code is working, here is an alternate list of tuples, in which there are two identical display strings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;i_listTuplePair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"One"&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Two"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Three"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Two"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In that case, all four options are in the ComboBox and can each return a distinct key - i.e. the fact that two display strings are there is worked around.&lt;/p&gt;

&lt;p&gt;Caveat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the mechanism for this is very simple, and &lt;strong&gt;would&lt;/strong&gt; fail for some combinations in the source tuples. I will have to decide whether to write something more robust.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The List of Tuples with a Repeated Key
&lt;/h4&gt;

&lt;p&gt;And similarly here is an alternate list of tuples, in which there are two identical key values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;i_listTuplePair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"One"&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Two"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Three"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Four"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, as per the logic I wrote in, the second appearance of the key &lt;code&gt;2&lt;/code&gt; will be omitted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method B - using the index of the selection
&lt;/h2&gt;

&lt;p&gt;In Method A we just worked around the issue of a Combobox "returning" one of the display values.&lt;/p&gt;

&lt;p&gt;But with the standard Tkinter Combobox we do also have the option of pulling out the index of the selected value. Can that be used to make a better method?&lt;/p&gt;

&lt;h3&gt;
  
  
  Air code
&lt;/h3&gt;

&lt;p&gt;For this, I copied the finished Method A code and changed the definition of &lt;code&gt;getSelectedKey&lt;/code&gt; to use &lt;code&gt;current()&lt;/code&gt; and then thought my way backwards about what data structure it would require to exist. I chose to build two lists in parallel, so that corresponding keys and show strings would be at the same index number.&lt;/p&gt;

&lt;p&gt;That gave me the following code, ready to implant into my program. I reversed part of the name "TuplePair" becoming "PairTuple" so that the usage calls could be easily swapped to test it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PairTupleCombobox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;m_tkntr_ttk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Combobox&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_process_listTuplePair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip_listTuplePair&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;r_list_keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; 
        &lt;span class="n"&gt;r_list_shows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; 
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ip_listTuplePair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;r_list_keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;r_list_shows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r_list_keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r_list_shows&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_listTuplePair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_defaultKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_list_keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_list_shows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_process_listTuplePair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;p_listTuplePair&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;textvariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_StringVar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'values'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_list_shows&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# still need to set the default value from the nominated key
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getSelectedKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;i_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_list_keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;i_index&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Actual code
&lt;/h3&gt;

&lt;p&gt;And here is the final code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PairTupleCombobox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;m_tkntr_ttk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Combobox&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_process_listPairTuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip_listPairTuple&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;r_list_keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; 
        &lt;span class="n"&gt;r_list_shows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; 
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ip_listPairTuple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;r_list_keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;r_list_shows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;tpl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r_list_keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r_list_shows&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_listPairTuple&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_defaultKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_list_keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_list_shows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_process_listPairTuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;p_listPairTuple&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'values'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_list_shows&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# still need to set the default value from the nominated key
&lt;/span&gt;        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;t_default_key_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_list_keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;p_defaultKey&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; 
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;t_default_key_index&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getSelectedKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;i_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i_list_keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;i_index&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main change is that once it had passed initial testing, I add a &lt;code&gt;try&lt;/code&gt; clauses to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;handle finding the index of the default value and using &lt;code&gt;current&lt;/code&gt; as a setter&lt;/li&gt;
&lt;li&gt;handle in case no default was set and no selection was made&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;As an exercise, this was a nice combination of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;giving myself the ability to rework and simplify most of the places where I use a Combobox control&lt;/li&gt;
&lt;li&gt;a rediscovery that using classes to rework other classes wasn't quite as tricky as I'd expected &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I do think I should find a better name for my new classes and/or choose which one of them to keep long term. &lt;/p&gt;

</description>
      <category>python</category>
      <category>tkinter</category>
    </item>
    <item>
      <title>A Beginner's Guide to Copyright, Programming and Software - Short Version</title>
      <dc:creator>geraldew</dc:creator>
      <pubDate>Wed, 05 Oct 2022 13:50:33 +0000</pubDate>
      <link>https://forem.com/geraldew/a-beginners-guide-to-copyright-programming-and-software-short-version-3cj0</link>
      <guid>https://forem.com/geraldew/a-beginners-guide-to-copyright-programming-and-software-short-version-3cj0</guid>
      <description>&lt;p&gt;This is intended as a bullet-point guide to the concepts of copyright that someone new to programming will need to understand. An interest of mine is Free and Open Source Software (FOSS) so this guide includes points about the copyright context of that.&lt;/p&gt;

&lt;p&gt;This is part of a planned set of postings - starting with the essentials and then exploring more detail for those who are interested. The intended titles are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Short Version&lt;/strong&gt; (i.e. this post)&lt;/li&gt;
&lt;li&gt;Medium Version&lt;/li&gt;
&lt;li&gt;Edge Cases&lt;/li&gt;
&lt;li&gt;Myths and Misunderstandings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As the various parts are published I will amend this posting to provide links in the list above - so if you don't seen any links, then then others are still yet to come.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Short Version
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Law
&lt;/h3&gt;

&lt;p&gt;Bear in mind that this will involve some over-simplifications - the intention is to be brief but prepare an understanding before hitting the complexities.&lt;/p&gt;

&lt;p&gt;Here are the dot points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copyright by now has a fairly uniform legal approach around the world.&lt;/li&gt;
&lt;li&gt;By default, anything created has copyright automatically applied at the point of creation.&lt;/li&gt;
&lt;li&gt;By default, copyright means that the creator "owns" the copyright and any and &lt;strong&gt;nearly all&lt;/strong&gt; re-distribution &lt;strong&gt;is only legal if&lt;/strong&gt; there is authorisation from that owner.&lt;/li&gt;
&lt;li&gt;With private individuals, the "owner" is that private individual but with employees creating things in the course of their employment, the "owner" is usually the employer.&lt;/li&gt;
&lt;li&gt;An owner can sell their copyright to another party who then "holds" the copyright.&lt;/li&gt;
&lt;li&gt;Copyright does &lt;strong&gt;not&lt;/strong&gt; apply to titles.&lt;/li&gt;
&lt;li&gt;Copyright &lt;strong&gt;does&lt;/strong&gt; apply to translations.&lt;/li&gt;
&lt;li&gt;Copyright laws were mainly set up long before software became a major thing, but were adapted enough to explicitly cover software too.&lt;/li&gt;
&lt;li&gt;For software, copyright is independent of whether you get to see the source code.&lt;/li&gt;
&lt;li&gt;It is possible for the owner to embed a license statement in the copyrighted work that can grant various rights and freedoms to others, perhaps without requiring either contact or negotiation.&lt;/li&gt;
&lt;li&gt;There is a broad set of of such licenses that are collectively known as providing "software freedom".&lt;/li&gt;
&lt;li&gt;In practice, there are two independent organisations who provide guidance about such software licenses - the Free Software Foundation (FSF) and the Open Source Initiative (OSI).&lt;/li&gt;
&lt;li&gt;While there are many, many variations on the software freedom licenses, they tend to fall into two main types - most commonly referred to as "copyleft" and "permissive".&lt;/li&gt;
&lt;li&gt;Only a copyright owner can re-issue content with a different license.&lt;/li&gt;
&lt;li&gt;Not all countries have a concept of "public domain" in their copyright laws, and those that do are not all the same about it.&lt;/li&gt;
&lt;li&gt;Nearly all copyright laws specify a finite period of time for which copyright applies to a work.&lt;/li&gt;
&lt;li&gt;But, many countries have repeatedly changed their copyright laws so as to keep pushing the point where copyright ceases for a work further and further into the future.&lt;/li&gt;
&lt;li&gt;Somewhat similar to how titles are not copyrightable, many copyright laws allow for limited unauthorised publishing (e.g. of small extracts, or for specific purposes) but the terms, concepts and conditions vary quite a lot.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Those are the core principles that you need to know in order to understand the basics of copyright as a legal concept.&lt;/p&gt;

&lt;p&gt;However, that leaves many possible complications, from interpretations of details, international variations, practical likelihoods, application to software, etc - those will instead be tackled in the "Medium" part.&lt;/p&gt;

&lt;p&gt;Also, while most of that list is quite stark and bluntly stated, many misunderstandings and myths are often circulated - some of those will be tackled in the "Myths" part (e.g. suggestions that people other than the copyright owner can "re-license" a work).&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Laws
&lt;/h3&gt;

&lt;p&gt;As a quick aside:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The term "intellectual property" (or "IP") is somewhat made-up, so as to cover copyright, trademark and patent laws together;&lt;/li&gt;
&lt;li&gt;Trademark laws vary greatly across countries, both legally and in practice;&lt;/li&gt;
&lt;li&gt;Patent laws are about ideas and incidentally requires the ideas to be openly declared. By comparison, copyright is about the particular expressions - e.g. the text, pictures, videos, sounds, instructions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where both of those other "intellectual property" legal concepts have their own complications and variations.&lt;/p&gt;

&lt;p&gt;Some other legal concepts that have intersections with software are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;contract law - particularly where one party is contracted by another to create a copyrightable work.&lt;/li&gt;
&lt;li&gt;consumer law - where a copyrightable work is sold/given from a producer to a customer/user.&lt;/li&gt;
&lt;li&gt;censorship laws - which might inhibit or prohibit the publishing of works independent of copyright&lt;/li&gt;
&lt;li&gt;border laws - which might inhibit or prohibit the transit of works across national borders independent of copyright&lt;/li&gt;
&lt;li&gt;(and perhaps many more such)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other than being mentioned above, these other laws will not be covered by these notes. For one thing, they vary enormously from country to country. &lt;/p&gt;

&lt;h3&gt;
  
  
  Paying for things
&lt;/h3&gt;

&lt;p&gt;Generally, copyright law doesn't say much about needing to pay for things. Instead it empowers the copyright owner to include payment requirements when authorising distribution and replication, such as via contracts and licenses.&lt;/p&gt;

&lt;p&gt;How this plays out in practice varies enormously. For example, the replication might be physical (e.g. a paper book) and the payment physical (cash) with the bookseller returning a portion of the sale. Or it might all be virtual (downloaded software) and the payment virtual (credit card transaction online) direct to the producer (perhaps to get a valid unlock code to input to the software). Sometimes the model might be to not inhibit replication but to require allowing the copyright owner to "audit" the amount of usage and exact a corresponding payment. The mechanisms of such &lt;em&gt;proprietary&lt;/em&gt; use of copyright are many and varied. Contracts, licenses and End User License Agreements (EULA) can be very complex.&lt;/p&gt;

&lt;p&gt;The key is that &lt;strong&gt;unauthorised&lt;/strong&gt; replication and distribution is a violation of copyright, independent of the exchange of funds. &lt;/p&gt;

&lt;h3&gt;
  
  
  Free and Open Source Software (FOSS)
&lt;/h3&gt;

&lt;p&gt;There is a set of core concepts that are generally known as "the four freedoms": A program is free open source software if the program's users have the four essential freedoms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;freedom to run the program as you wish, for any purpose (freedom 0).&lt;/li&gt;
&lt;li&gt;freedom to study how the program works, and change it so it does your computing as you wish (freedom 1). &lt;em&gt;Access to the source code is a precondition for this&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;freedom to redistribute copies so you can help others (freedom 2).&lt;/li&gt;
&lt;li&gt;freedom to distribute copies of your modified versions to others (freedom 3). By doing this you can give the whole community a chance to benefit from your changes. &lt;em&gt;Access to the source code is a precondition for this&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FOSS license statements, embedded in a work, effectively provide a &lt;strong&gt;pre-authorised&lt;/strong&gt; negotiation, meaning neither creator nor adopter has to contact each other about the applicable conditions.&lt;/p&gt;

&lt;p&gt;Note here that access to the source code is a &lt;em&gt;mechanism&lt;/em&gt; to enable freedom.&lt;/p&gt;

&lt;p&gt;By the way, you should ignore use of the term "commercial" software, as the software freedom concepts do not impede any parties from making financial (i.e. commercial) arrangements. The opposite of FOSS is &lt;strong&gt;proprietary&lt;/strong&gt; where the defaults of copyright law apply, and all arrangements require an active negotiation.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Two Main License Types
&lt;/h4&gt;

&lt;p&gt;As said earlier, the major cleft among FOSS licenses are between two approaches. The distinction comes down to quite who is granted the most freedom:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;with "copyleft" licenses the emphasis is on the &lt;strong&gt;end-user&lt;/strong&gt;, ensuring that they are &lt;strong&gt;always&lt;/strong&gt; passed the four freedoms;&lt;/li&gt;
&lt;li&gt;with "permissive" licenses, the emphasis is on other developers/programmers, including allowing them to &lt;strong&gt;not&lt;/strong&gt; pass on the four freedoms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are distinct strategies with applicability to different situations. There is much discussion among the FOSS community about when each is the more suitable choice, or even whether it matters.&lt;/p&gt;

&lt;p&gt;The thing that everyone agrees about, is that the copyright owner gets to choose the license to apply - essentially because copyright law enforces that privilege.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Two Philosophies
&lt;/h4&gt;

&lt;p&gt;About the two organisations who are the stewards of Free Open Source Software:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Free Software Foundation came first, they set out the core ideas and also created the General Public License (probably the most important "copyleft" license).&lt;/li&gt;
&lt;li&gt;The Open Source Initiative came later and, using the same four freedoms, emphasised the practical benefits of software freedom and also offered a service for approving specific licenses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The two organisations have differing purposes and activities - complementary in effect. I recommend reading their own web sites for clarity - I see many inaccurate descriptions made about them by others.&lt;/p&gt;

&lt;p&gt;My view is that you can mostly ignore any bickering between the Free Software and Open Source camps. They agree about most of what software freedom is all about. While their independent focus means they don't agree 100% - as a qualitative guess I would say the agreement is over 95% - indeed it is quite hard to find a license that one approves that the other does not.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Note they they also &lt;strong&gt;both&lt;/strong&gt; support &lt;em&gt;both&lt;/em&gt; "copyleft" and "permissive" licenses (and always have done)&lt;/li&gt;
&lt;li&gt;Since this piece was initially published, I have published an article listing some of the differing license approvals by the FSF and OSI - see &lt;a href="https://geraldew.wordpress.com/2023/10/22/how-many-and-of-what-nature-are-the-license-approval-differences-of-the-fsf-and-the-osi/"&gt;How many and of what nature are the license approval differences of the FSF and the OSI ?&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creative Commons
&lt;/h3&gt;

&lt;p&gt;The innovation of using embedded licenses for software - to provide software freedom - was so successful that an effort arose to do something similar for non-software content.  This led to the creation of a set known as Creative Commons licenses. These licenses enable authors of creative works to specify which rights they reserve and which rights they waive for the benefit of recipients or other creators.&lt;/p&gt;

&lt;p&gt;This has also become sufficiently successful to now be quite frequently used for application to the documentation of software.&lt;/p&gt;

&lt;p&gt;The core mechanism is the same as for software freedom licenses - of embedding a quotation of the license into the body of the copyrighted work. The distinction is about clarity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;of having a software license that talks about software concepts for applying to software&lt;/li&gt;
&lt;li&gt;of having a content license that does &lt;strong&gt;not&lt;/strong&gt; talk about software concepts for applying to things that are not-software.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Short Version Coda
&lt;/h3&gt;

&lt;p&gt;Life is complex, and so the above opens the door for a lot of questions about the details. Those will be tackled in the next part. &lt;/p&gt;

&lt;p&gt;Also, to reiterate, this "short" version is deliberately simplistic - and was left so, rather than litter it with footnotes and hyperlinks. Ideally I'd publish the full set all at once, but to avoid the overall delay of that, I will publish the parts only as and when I'm happy with them.&lt;/p&gt;

&lt;p&gt;The reason I've written this, is because while I've seen a great many attempts at explaining Open Source Software or Free Software, nearly all presume a familiarity with &lt;strong&gt;copyright&lt;/strong&gt;. So I wanted to try reversing that - to explain copyright &lt;em&gt;first&lt;/em&gt; while placing FOSS in that context.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>copyright</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
