【问题标题】:moving sql logic to backend - bash将 sql 逻辑移动到后端 - bash
【发布时间】:2021-03-28 10:12:26
【问题描述】:

其中一个 sql 逻辑正在移动到后端,我需要使用 shell 脚本生成报告。 为了便于理解,我将其简化如下。

我的输入文件 - sales.txt(id、价格、月份)

101,50,2019-10
101,80,2020-08
101,80,2020-10
201,100,2020-09
201,350,2020-10

每个 id 的输出应为 6 个月的窗口,例如 t1=2020-07 和 t2=2020-12

101,50,2020-07
101,80,2020-08
101,80,2020-09
101,80,2020-10
101,80,2020-11
101,80,2020-12
201,100,2020-09
201,350,2020-10
201,350,2020-11
201,350,2020-12

对于 id 101,虽然没有 2020-07 的条目,但它应该取自销售文件中可用的上个月的值。 所以 2019-10 的 price=50 用于 2020-07。

对于201,第一个条目本身是从 2020-09 开始的,因此 2020-08 和 2020-07 不适用。 只要有差距,就应该传播上个月的值。

我正在尝试使用 awk 来解决这个问题,我正在创建一个可重复使用的脚本 util.awk,如下所示 要生成缺失值,请将其传递给 sort 命令,然后再次使用 util.awk 进行最终输出。

util.awk

function get_month(a,b,t1) { return strftime("%Y%m",mktime(a " " b t1)) } 
BEGIN { ss=" 0 0 0 "; ts1=" 1 " ss; ts2=" 35 " ss ; OFS="," ; x=1 } 
{ 
  tsc=get_month($3,$4,ts1);
  if ( NR>1 && $1==idp )
  {
  if( tsc == tsp) { print $1,$2,get_month($3,$4,ts1); x=0  }
  else { for(i=tsp; i < tsc; i=get_month(j1,j2,i) )  
         { 
       j1=substr(i,1,4); j2=substr(i,5,2); 
           print $1,tpr,i;
         }
       }
   }

  tsp=get_month($3,$4,ts2);  
  idp=$1;
  tpr=$2;
  if(x!=0) print $1,$2,tsc
  x=1;
  
}

但它无限运行awk -F"[,-]" -f utils.awk sales.txt

虽然我在 awk 中尝试过,但我也欢迎其他适用于 bash 环境的答案。

【问题讨论】:

  • sql logic is moving to backend 是什么意思?当我听到/看到backend 时,我想到了数据库服务器,但是与(sql)数据库相关的问题中没有任何内容......??您的输入文件中有多少行 (sales.txt)?
  • 当我运行它生成的代码时:101,50,201910101,50,201911101,50,196912,最后一个 (101,50,196912) 一遍又一遍地重复(无限循环?); 196912 看起来像纪元时间减去 1 秒,这很可能是由于 mktime() (???) 的缺失/空/空/无效参数而发生的;第一次通过for 循环,j1j2 是未定义的,所以不确定这对for 增量组件有什么作用; for 循环的增量为 i=get_month(j1,j2,i),其中 i 是一个 6 位数字,如 YYYYMM,这似乎不是函数的有效参数 (???) ...跨度>
  • ...我建议在战略位置添加一些print 命令以显示您的变量...从中您应该能够了解逻辑在哪里导轨;为了防止主调用滚动到屏幕之外,请考虑:awk -F"[,-]" -f utils.awk sales.txt | head -100(调整 head 参数以显示足够的信息以用于多个循环)

标签: bash shell perl awk


【解决方案1】:

总体规划:

  • 假设:sales.txt 已按第一列(按数字)排序
  • 用户提供要显示的最小->最大日期范围(awk 变量mindtmaxdt
  • 对于不同的 id 值,我们会将所有价格和日期加载到一个数组中 (prices[])
  • 日期将用作关联数组的索引来存储价格 (prices[YYYY-MM])
  • 一旦我们读取了给定id 的所有记录...
  • 按索引对prices[] 数组进行排序(即按YYYY-MM 排序)
  • 查找最大日期小于mindt的价格(另存为prevprice
  • 对于mindtmaxdt(含)之间的每个日期,如果我们有价格,则显示它(并保存为prevprice)否则...
  • 如果我们没有价格但我们有prevprice,则使用此prevprice 作为当前日期的price(即,用之前的价格填补空白)

一个(GNU)awk 想法:

mindate='2020-07'
maxdate='2020-12'

awk -v mindt="${mindate}" -v maxdt="${maxdate}" -v OFS=',' -F',' ' 

# function to add "months" (number) to "indate" (YYYY-MM)

function add_month(indate,months) {

    dhms="1 0 0 0"                                     # default day/hr/min/secs
    split(indate,arr,"-")
    yr=arr[1]
    mn=arr[2]

    return strftime("%Y-%m", mktime(arr[1]" "(arr[2]+months)" "dhms))
}

# function to print the list of prices for a given "id"

function print_id(id) {

    if ( length(prices) == 0 )                         # if prices array is empty then do nothing (ie, return)
       return

    PROCINFO["sorted_in"]="@ind_str_asc"               # sort prices[] array by index in ascending order

    for ( i in prices )                                # loop through indices (YYYY-MM)
        { if ( i < mindt )                             # as long as less than mindt
             prevprice=prices[i]                       # save the price
          else
             break                                     # no more pre-mindt indices to process
        }

    for ( i=mindt ; i<=maxdt ; i=add_month(i,1) )     # for our mindt - maxdt range
        { if ( !(i in prices) && prevprice )          # if no entry in prices[], but we have a prevprice, then ...
             prices[i]=prevprice                      # set prices[] to prevprice (ie, fill the gap)

          if ( i in prices )                          # if we have an entry in prices[] then ...
             { prevprice=prices[i]                    # update prevprice (for filling future gap) and ...
               print id,prices[i],i                   # print our data to stdout
             }
        }
}

BEGIN { split("",prices) }                             # pre-declare prices as an array

previd != $1 { print_id(previd)                        # when id changes print the prices[] array, then ...
               previd=$1                               # reset some variables for processing of the next id and ...
               prevprice=""
               delete prices                           # delete the prices[] array
             }

             { prices[$3]=$2 }                         # for the current record create an entry in prices[]

END   { print_id(previd) }                             # flush the last set of prices[] to stdout
' sales.txt

注意:假设sales.txt 按第一个字段排序(数字);如果不是这样,那么最后一行应更改为' &lt;(sort -n sales.txt)

这会生成:

101,50,2020-07
101,80,2020-08
101,80,2020-09
101,80,2020-10
101,80,2020-11
101,80,2020-12
201,100,2020-09
201,350,2020-10
201,350,2020-11
201,350,2020-12

【讨论】:

  • @makp-fuso.. 文件将按 id 和日期排序。您的解决方案现在可以工作了.. 将彻底测试
【解决方案2】:

我希望我能理解你的问题。以下 awk 应该可以解决问题

$ awk -v t1="2020-07" -v d="6" '
     function next_month(d,a) { 
         split(d,a,"-"); a[2]==12?a[1]++ && a[2]=1 : a[2]++
         return sprintf("%0.4d-%0.2d",a[1],a[2])
     } 
     BEGIN{FS=OFS=",";t2=t1; for(i=1;i<=d;++i) t2=next_month(t2)}
     {k[$1]}
     ($3<t1){a[$1,t1]=$2}
     (t1 <= $3 && $3 < t2) { a[$1,$3]=$2 }
     END{ for (key in k) {
            p=""; t=t1; 
            for(i=1;i<=d;++i) { 
               if(p!="" || (key,t) in a) print key, ((key,t) in a ? p=a[key,t] : p), t
               t=next_month(t)
            }
          }
     }' input.txt

我们实现了一个简单的函数next_month,它根据YYYY-MM 的格式计算下个月。根据d 个月的持续时间,我们计算应该在BEGIN 块中显示的时间段。感兴趣的时间段是t1 &lt;= t &lt; t2

每次我们读取记录/行时,我们都会跟踪他已处理的密钥并将其存储在数组k 中。这样我们就知道到目前为止已经看到了哪个键。

对于感兴趣的时间段之前的所有时间,我们将值存储在数组a 中,索引为(key,t1),而对于所有其他时间,我们将值存储在数组a 中,键为@ 987654331@.

当文件被完全处理后,我们只需循环遍历所有键并打印输出。我们使用了一些逻辑来检查月份是否在原始文件中列出。

注意:输出将按键按时间排序,但键的出现顺序与原始文件中的顺序不同。

【讨论】:

  • 是的,它有效.. 非常感谢您的回复.. END 部分有点棘手.. 但会花一些时间来理解..
猜你喜欢
  • 2020-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-02
  • 2017-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多