# Purpose: generate a simple statistic for all static queries
# contact: stefan.reil@de.ibm.com

# version 6 (04/12/2009)
# took into account following 6.0.0 specifics
# Delayed query indicator = CWPCM0018W
# runtime can contain "," as digit grouping 
# SQLs start on first line after runtime value


# USAGE
# generates a summary for each query with overall runtime, number of executions and avg runtime
# output will be sorted automatically on overall runtime in ascending order
# Dynamic queries can be differentiated by providing optional parameter "leng"
# If db.log tracks only DELAYED QUERIES, option "delayed=1" can be used to diagnose those

# ===> awk -f sumall.awk [leng=<dynamic queries first n characters parameter>] [delayed=1] db.log[*] 

# the script prints a first and last processed querytimestamp at the end.  This timestamp will show the 
# correct timerange covered in the db.log files, when they are given to the script in the correct order
# start with oldest logs first e.g.
# awk -f sumall.awk leng=70 db.log.3 db.log.2 db.log.1 db.log 


BEGIN{
	cnt = 0;
	config = 0;
	sqlstr = "";
}
{
	# set variables according to input parameters
	if(config == 0){
		if(leng <=0){
			leng = 0;
		}
		if(delayed <=0){
			# Assumption: non delayed queries will have string "Time:" on first line
			SEARCHSTRING = "Time:";
		}
		else {
			# Assumption: delayed queries will have string "CWPCM0018W" or "DELAYED" on first line
			SEARCHSTRING = "CWPCM0018W|DELAYED";
		}
		config = 1;
	}
	timeopenstr="(";
	pos = match($0,SEARCHSTRING);
	if(pos > 0){
		# get the query runtime - first element in splitarray is addressed with v[1]
		# Assumption: actual runtime value is followed by "ms"
		split(substr($0,index($0,timeopenstr)+1),v,"ms");
		# special time calculation needed as time may contain digit grouping separators ","
		# Assumption: "," is used for digit grouping 
		nsplit=split(v[1],z,",");
		t_time = 0;
		t_m = 1;
		for (i=nsplit; i>0;i--){
			t_time+=(z[i] * t_m);
			t_m*=1000;
		}
		time = t_time;
		# end of special time calculation
		
		# get queries execution timestamp
		stamp1 = $1;
		stamp2 = $2;
		# remember the first processed queries timestamp
		if (cnt <= 1){
			start1 = stamp1;
			start2 = stamp2;
			cnt =5;
		}
		# sometimes the Static identifier is on the same line as the timevalue
		posname = index($0,"name:");
		if(posname >0){
			split($0,z,"name: ");
			sql[z[2],1]+=time;
			sql[z[2],2]+=1;
		}

		else{	
			SEPERATOR = "\\n";
			# in MDM 6.0.0 the SQL might start in first line after the runtime
			if (index($0,SEPERATOR) > 0){
				MM = substr($0,index($0,SEPERATOR)+2)
				$0 = MM;	# set $0 to contain only the SQL part
			}
			# otherwise continue on next line to get static or dynamic SQL identifier
			else {
				getline;
			}
			if($1 ~ /Static/){
				sql[$4,1]+=time;
				sql[$4,2]+=1;
			}
			else {
				if($1 ~ /Dynamic/ && time >= 0){
					# get the full SQL string from all consecutive lines, till end terminator ] is found
					while (index($0,"]")<=0){
						for (i=1; i<NF+1; i++){
							sqlstr = sqlstr " " $i;
						}
						getline;
					}
					# this line is necessary to get the SQL part on the last line of the respective SQL
					if (index($0,"]") > 0){
						sqlstr = sqlstr " " substr($0,1,index($0,"]"));
					}
					# remember only the first n characters of the SQL string as defined with leng parameter
					sqlshort = "Dynamic " substr(sqlstr,index(sqlstr,"["),leng);
					sqlstr = "";
				}
				sql[sqlshort,1] += time;
				sql[sqlshort,2] += 1;
				dynsum += time;
				dyncnt += 1;
			}
		}
	}
}
END{
	# create the unique list of sqlnames
	command = "sort -n | uniq > xysumall_sort_tmpxy"	# uniq needed to avoid duplication of sql names due to awk array usage
	for(x in sql){
		split(x, splitv, SUBSEP);
		print sql[splitv[1],1]";" splitv[1] | command;
	}
	close (command);

	# read sorted list
	cnt=1;
	while ((getline line < "xysumall_sort_tmpxy") > 0){
		split(line,vec,";");
		mysortl[cnt] = vec[2];
		cnt++;
	}
	#delete the temp sort file from filesystem as it is no longer used
	system("rm xysumall_sort_tmpxy 2>&1 > /dev/null");

	# print sqls in ascending runtime order ==> using mysortl
	printf("\nStatistics per query\n");
	printf("%-10s %-10s %-10s\n","millisecs","count","average runtime");
	printf("%-10s %-10s %-10s %-10s \n", "sum(ms)", "cnt", "avg(ms)", "QUERY");	
	for (e=1; e<cnt; e++){
		printf("%-10d %-10d %-10d %-20s\n", sql[mysortl[e],1], sql[mysortl[e],2],sql[mysortl[e],1] / sql[mysortl[e],2] ,mysortl[e]);
		totalsum += sql[mysortl[e],1];
		totalcnt += sql[mysortl[e],2];
	}
	printf("%-10s %-10s %-10s %-10s \n", "sum(ms)", "cnt", "avg(ms)", "QUERY");	

	# need to avoid division by 0
	if(totalcnt >0){
		printf("\nStatistical values for all queries\n");
		printf("%-10s %-10s %-10s\n","millisecs","count","average runtime");
		printf("%-10s %-10s %-10s %-10s","(ms)","(cnt)","(avg)","Total runtime in hours:");
		printf("\n %-10d %-10d %-10d %-5f \n\n", totalsum, totalcnt, totalsum / totalcnt, totalsum/3600000);
	}
	if(dyncnt>0){
		printf("out of which dynamic queries account for \n");
		printf("%-10s %-10s %-10s %-10s\n","(ms)","(cnt)","(avg)","Total runtime in hours:");
		printf("%-10d %-10d %-10d %-5f \n\n", dynsum, dyncnt, dynsum / dyncnt, dynsum/3600000);
	}
	print "first processed querytimestamp: "start1, start2  ;
	print "last  processed querytimestamp: "stamp1, stamp2 ;
	
	# printf("\n use \"awk -f sumsql.awk leng=<stringlength> db.lo*\"  to distinguish Dynamic queries with specified stringlength");
	# printf("\n use \"awk -f sumsql.awk leng=<stringlength> delayed=1 db.lo*\"  to parse DELAYED QUERIES only");
}
