sum total column of a table - ruby-on-rails

I have a this query in rails:
def self.nominas(params)
query = select("payroll_employees.*,payroll_employees.total, concat(e.name, ' ', e.surname, ' ', e.other_surname) as nombre_empleado, ec.code as contrato, CASE WHEN payroll_employees.is_wage_integral = TRUE THEN 'Si' WHEN payroll_employees.is_wage_integral = FALSE THEN 'No' END AS salario_es_integral, pc.integral_wage AS p_c_integral_wage")
query = query.joins("inner join payrolls p on (p.id = payroll_employees.payroll_id) inner join employee_contracts ec on (ec.id = payroll_employees.employee_contract_id) inner join employees e on (e.id = ec.employee_id) inner join payroll_companies pc on (pc.company_id = p.company_id) ")
query = query.where('p.id = :nomina', {nomina: params[:id] })
query = query.group(:id, 'e.name', 'e.surname', 'e.other_surname', 'ec.code', 'pc.integral_wage')
query = query.having("(lower(concat(e.name, ' ', e.surname, ' ', e.other_surname)) LIKE :campo_busqueda or :campo_busqueda = '') OR (lower(ec.code) LIKE :campo_busqueda or :campo_busqueda = '')", {campo_busqueda: "%#{params[:buscador].try(:downcase)}%"})
query = query.order('nombre_empleado')
end
in which I have a column "total", and I need to do the summation of the column "total" of all the records that the query brings me, my problem comes at the time of doing this:
#payroll_employees = PayrollEmployee.nominas(params)####
#sum_total = #payroll_employees.sum(:total)
it brings me something like this:
{[345, "Angel", "BONILLA", "MONTAÑO", "2010", true]=>0.106215575500000000000001e7, [079, "Bill f", "CABRERA", "RICO", "1846", true]=>0.1330346e7, ...
it seems to me that it is because my query has a group. Is it possible to do the summation and get a single number instead of a grouped array of totals?

Try to use sum function on total sum(payroll_employees.total)
def self.nominas(params)
query = select("payroll_employees.*,sum(payroll_employees.total) as total, concat(e.name, ' ', e.surname, ' ', e.other_surname) as nombre_empleado, ec.code as contrato, CASE WHEN payroll_employees.is_wage_integral = TRUE THEN 'Si' WHEN payroll_employees.is_wage_integral = FALSE THEN 'No' END AS salario_es_integral, pc.integral_wage AS p_c_integral_wage")
query = query.joins("inner join payrolls p on (p.id = payroll_employees.payroll_id) inner join employee_contracts ec on (ec.id = payroll_employees.employee_contract_id) inner join employees e on (e.id = ec.employee_id) inner join payroll_companies pc on (pc.company_id = p.company_id) ")
query = query.where('p.id = :nomina', {nomina: params[:id] })
query = query.group(:id, 'e.name', 'e.surname', 'e.other_surname', 'ec.code', 'pc.integral_wage')
query = query.having("(lower(concat(e.name, ' ', e.surname, ' ', e.other_surname)) LIKE :campo_busqueda or :campo_busqueda = '') OR (lower(ec.code) LIKE :campo_busqueda or :campo_busqueda = '')", {campo_busqueda: "%#{params[:buscador].try(:downcase)}%"})
query = query.order('nombre_empleado')
end

Related

Arel: I get "stack level too deep" error when I try to get SQL from Arel

First I'd like to describe idea of what I am trying to do. I have "jobstat_jobs" table where I store the information about computing job perfomance. I am trying to compose 2 queries: 1) jobs grouped by project 2) jobs grouped by project and state. Then these queries are inner joined and I want to display share of jobs of each state among all jobs. I implemented it using ActiveRecord and raw sql, but I can't do it with arel. I get the "stack level too deep" on the "joined.to_sql" line.
members = Core::Member.arel_table
jobs = Perf::Job.arel_table
cool_relation = jobs.where(jobs[:state].not_in(%w[COMPLETETED RUNNING unknown]))
relation = cool_relation.join( Arel::Nodes::SqlLiteral.new <<-SQL
INNER JOIN core_members ON core_members.login = jobstat_jobs.login
SQL
).join(Arel::Nodes::SqlLiteral.new <<-SQL
RIGHT JOIN sessions_projects_in_sessions ON
sessions_projects_in_sessions.project_id = core_members.project_id
SQL
).group(members[:project_id]).project(members[:project_id].as('id'))
hours = '(extract(epoch from (end_time - start_time))/ 3600)'
selections = {
node_hours: "(sum((#{hours})*num_nodes))",
jobs: "count(jobstat_jobs.id)"
}
selections.each do |key, value|
relation = relation.project(
Arel::Nodes::SqlLiteral.new(value).as(key.to_s)
)
end
state_relation = relation.project(jobs[:state].as('state'))
.group(jobs[:state])
s = state_relation.as('s')
pp ActiveRecord::Base.connection.exec_query(state_relation.to_sql).to_a
joined = relation.join(s)
.on(jobs[:id].eq(s[:id]))
.project(s[:id], s[:state])
puts joined.to_sql
joined
I noticed the strange thing. When I replace "joined = relation" with "jobs.where(jobs[:state].not_in(%w[COMPLETETED RUNNING unknown]))" it works. But when I replace "joined = relation" with "joined = cool_relation" it doesn't work and I get "stack level too deep" (these 2 replacements are almost the same).
Arel v 9.0.0, Postgresql
My problem is that I expected arel to create a new object each time I chain method(like ActiveRecord::Relation).
Just add #clone method here:
joined = relation.clone.join(s)
.on(jobs[:id].eq(s[:id]))
.project(s[:id], s[:state])
I got SQL string, but it was wrong and there were exceptions on database level. Now my code is following:
members = Core::Member.arel_table
jobs = Perf::Job.arel_table
cool_relation = jobs.where(jobs[:state].not_in(%w[COMPLETETED RUNNING unknown]))
relation = cool_relation.join( Arel::Nodes::SqlLiteral.new <<-SQL
INNER JOIN core_members ON core_members.login = jobstat_jobs.login
SQL
.gsub("\n", ' ')).join(Arel::Nodes::SqlLiteral.new <<-SQL
RIGHT JOIN sessions_projects_in_sessions ON
sessions_projects_in_sessions.project_id = core_members.project_id
SQL
.gsub("\n", ' ')).group(members[:project_id]).project(members[:project_id].as('id'))
hours = '(extract(epoch from (end_time - start_time))/ 3600)'
selections = {
node_hours: "(sum((#{hours})*num_nodes))",
jobs: "count(jobstat_jobs.id)"
}
selections.each do |key, value|
relation = relation.project(
# Arel::Nodes::SqlLiteral.new(value).as(key.to_s)
Arel::Nodes::SqlLiteral.new("(CAST(#{value} AS decimal))").as(key.to_s)
)
end
state_relation = relation.clone.project(jobs[:state].as('state'))
.group(jobs[:state])
s = state_relation.as('s')
n = relation.as('n')
pp ActiveRecord::Base.connection.exec_query(state_relation.to_sql).to_a
pp ActiveRecord::Base.connection.exec_query(relation.to_sql).to_a
manager = Arel::SelectManager.new
joined = manager.project(s[:id], s[:state])
.from(s)
.join(n).on(s[:id].eq(n[:id]))
selections.keys.each do |key|
joined = joined.project(s[key].as("s_#{key}"), n[key].as("n_#{key}"))
.project(s[key] / n[key].as("share_#{key}"))
end
puts joined.to_sql
joined
Pay attention to the #clone method used here too. When I remove #clone, project method affects relation variable too and I get wrong SQL because of that.
The joined.to_sql line produces the following and works as expected:
SELECT s."id", s."state", s."node_hours" AS s_node_hours,
n."node_hours" AS n_node_hours, s."node_hours" / n."node_hours" AS
share_node_hours, s."jobs" AS s_jobs, n."jobs" AS n_jobs,
s."jobs" / n."jobs" AS share_jobs FROM (SELECT "core_members".
"project_id" AS id, (CAST((sum(((extract(epoch from (end_time - start_time))/ 3600))*num_nodes)) AS decimal)) AS node_hours,
(CAST(count(jobstat_jobs.id) AS decimal)) AS jobs,
"jobstat_jobs"."state" AS state FROM "jobstat_jobs" INNER JOIN
core_members ON core_members.login = jobstat_jobs.login
RIGHT JOIN sessions_projects_in_sessions ON sessions_projects_in_sessions.project_id = core_members.project_id
WHERE "jobstat_jobs"."state" NOT IN ('COMPLETETED', 'RUNNING', 'unknown')
GROUP BY "core_members"."project_id", "jobstat_jobs"."state") s INNER JOIN
(SELECT "core_members"."project_id" AS id, (CAST((sum(((extract(epoch from
(end_time - start_time))/ 3600))*num_nodes)) AS decimal)) AS node_hours,
(CAST(count(jobstat_jobs.id) AS decimal)) AS jobs FROM "jobstat_jobs"
INNER JOIN core_members ON core_members.login = jobstat_jobs.login RIGHT JOIN sessions_projects_in_sessions ON
sessions_projects_in_sessions.project_id = core_members.project_id WHERE
"jobstat_jobs"."state" NOT IN ('COMPLETETED', 'RUNNING', 'unknown') GROUP BY
"core_members"."project_id") n ON s."id" = n."id"
Now that I understand the desired output here is how I would go about this
class Report
JOB_STATS = Arel::Table.new('jobstat_jobs')
CORE_MEMBERS = Arel::Table.new('core_members')
SESSIONS = Arel::Table.new('sessions_projects_in_sessions')
def additions
# This could be ported too if I knew the tables for end_time, start_time, and num_nodes
{
node_hours: Arel.sql("((extract(epoch from (end_time - start_time))/ 3600))*num_nodes").sum,
jobs: JOB_STATS[:id].count
}
end
def n
#n ||= _base_query.as('n')
end
def s
#s ||= _base_query
.project(JOB_STATS[:state])
.group(JOB_STATS[:state]).as('s')
end
def alias_columns
additions.keys.flat_map do |key|
[s[key].as("s_#{key}"),
n[key].as("n_#{key}"),
(s[key] / n[key]).as("share_#{key}")]
end
end
def query
Arel::SelectManager.new.project(
s[:project_id].as('id'),
s[:state],
*alias_columns
)
.from(s)
.join(n).on(s[:project_id].eq(n[:project_id]))
end
def to_sql
query.to_sql
end
private
def cast_as_decimal(value,alias_name:)
Arel::Nodes::NamedFunction.new(
"CAST",
[Arel::Nodes::As.new(value, Arel.sql('DECIMAL'))]
).as(alias_name.to_s)
end
def _base_query
JOB_STATS
.project(
CORE_MEMBERS[:project_id],
*additions.map {|k,v| cast_as_decimal(v, alias_name: k)})
.join(CORE_MEMBERS).on(CORE_MEMBERS[:login].eq(JOB_STATS[:login]))
.outer_join(SESSIONS).on(SESSIONS[:project_id].eq(CORE_MEMBERS[:project_id]))
.where(JOB_STATS[:state].not_in(['COMPLETETED', 'RUNNING', 'unknown']))
.group(CORE_MEMBERS[:project_id])
end
end
Result of Report.new.to_sql
SELECT
s."project_id" AS id,
s."state",
s."node_hours" AS s_node_hours,
n."node_hours" AS n_node_hours,
s."node_hours" / n."node_hours" AS share_node_hours,
s."jobs" AS s_jobs,
n."jobs" AS n_jobs,
s."jobs" / n."jobs" AS share_jobs
FROM
(
SELECT
"core_members"."project_id",
CAST(SUM(((extract(epoch from (end_time - start_time))/ 3600))*num_nodes) AS DECIMAL) AS node_hours,
CAST(COUNT("jobstat_jobs"."id") AS DECIMAL) AS jobs,
"jobstat_jobs"."state"
FROM
"jobstat_jobs"
INNER JOIN "core_members" ON "core_members"."login" = "jobstat_jobs"."login"
LEFT OUTER JOIN "sessions_projects_in_sessions" ON "sessions_projects_in_sessions"."project_id" = "core_members"."project_id"
WHERE
"jobstat_jobs"."state" NOT IN (N'COMPLETETED', N'RUNNING', N'unknown')
GROUP BY
"core_members"."project_id",
"jobstat_jobs"."state"
) s
INNER JOIN (
SELECT
"core_members"."project_id",
CAST(SUM(((extract(epoch from (end_time - start_time))/ 3600))*num_nodes) AS DECIMAL) AS node_hours,
CAST(COUNT("jobstat_jobs"."id") AS DECIMAL) AS jobs
FROM
"jobstat_jobs"
INNER JOIN "core_members" ON "core_members"."login" = "jobstat_jobs"."login"
LEFT OUTER JOIN "sessions_projects_in_sessions" ON "sessions_projects_in_sessions"."project_id" = "core_members"."project_id"
WHERE
"jobstat_jobs"."state" NOT IN (N'COMPLETETED', N'RUNNING', N'unknown')
GROUP BY
"core_members"."project_id"
) n ON s."project_id" = n."project_id"
This will also allow you further filter the resulting query like so:
rpt = Report.new
q = rpt.query.where(rpt.n[:jobs].gt(12))
q.to_sql
#=> "...same as above...WHERE n.\"jobs\" > 12"

.sum data from query

I'm trying to .sum a field from the following query:
def self.busqueda_general(params)
query = select('venta.Id,venta.TOTAL')
.distinct
.joins('left outer join detallevet ON venta.Documento=detallevet.Docto and venta.RutaId=detallevet.RutaId')
.where("(venta.RutaId = :rutaId or :rutaId = '') AND (detallevet.Articulo = :articulo or :articulo = '') AND (venta.CodCliente = :codcliente or :codcliente = '') AND (venta.IdEmpresa = :idempresa)",{rutaId: params[:search], articulo: params[:search3], codcliente: params[:search2], idempresa: params[:search6]})
query = query.where('venta.Fecha >= ? AND venta.Fecha <= ?', (params[:search4].to_date.beginning_of_day).strftime('%Y-%m-%d %T'), (params[:search5].to_date.end_of_day).strftime('%Y-%m-%d %T')) if params[:search4].present? and params[:search5].present?
query
end
In the method of the controller I call the query and sum it as follows:
#monto_total = Vent.busqueda_general(params).sum(:TOTAL)
but the problem is that the query is showing me records that are not repeated thanks to .distinct but with the .sum is adding up all the records including the repeated ones, ignoring the .distinct
Try this
#monto_total = Vent.busqueda_general(params).sum(:TOTAL).to_f
you can to use to_s (to convert in string) to_f -> float, to_i -> integer
You have a group by clause, so you get one sum(Total) for every combination of ID/Total.
.group('venta.Id,venta.TOTAL')
To me looks like you don't need this group by clause.
UPDATE::
query = where(Id: select('venta.Id')
.joins('left outer join detallevet ON venta.Documento=detallevet.Docto and venta.RutaId=detallevet.RutaId')
.where("(venta.RutaId = :rutaId or :rutaId = '') AND (detallevet.Articulo = :articulo or :articulo = '') AND (venta.CodCliente = :codcliente or :codcliente = '') AND (venta.IdEmpresa = :idempresa)",{rutaId: params[:search], articulo: params[:search3], codcliente: params[:search2], idempresa: params[:search6]}))
Change your main query to this keeping everything else same. Ideal way would be to move join on detallevet to where clause.

count and group by using ExcecuteQuery

how to get count number with group by activationDate?
i tried this query
def mapQuery6= new HashMap()
def query6 = "SELECT count(al.id) as totalCount,al.activationDate from Card al where and TO_DAYS(al.activationDate) > TO_DAYS(:from) and TO_DAYS(al.activationDate) <= TO_DAYS(:to) GROUP BY al.activationDate "
mapQuery6.from = params.from
def to = new Date().parse('dd/MM/yyyy HH:mm:ss', params.to+ " 05:00:00")
to.setDate(to.getDate() + 1)
mapQuery6.to = to
println "query 6 = "+query6
def activecard = Card.executeQuery(query6,mapQuery6)[0]
println "activecard = "+activecard
i want the result like this
if iam using Group by al.activationDate the the result when i printed was
activecard = [1, 2014-06-17 20:27:11.0]
and if without group by al.activationDate the result was
activecard = [6, 2014-06-27 20:27:11.0]
so i want ge a count number every month..
example 2014-07-xx have 3 count, 2014-06-xx 2, and 2014-05-xx only 1
Try grouping by month like
GROUP BY MONTH(al.activationDate)

Why I cannot use both ORDER BY and DISTINCT * in SQL?

I'm trying to do the following, and if I were to uncomment the distinct it will break. Also if I comment out the order and leave the distinct in, it will work.
Contestant.joins('INNER JOIN votes AS V ON V.contestant_id = contestants.id AND V.season_id = '+ season_number.to_s)
.joins('LEFT OUTER JOIN votes AS XV ON (XV.contestant_id = '+self.id.to_s+') AND (XV.tribal_council_key = V.tribal_council_key) AND XV.contestant_voted_for_id = V.contestant_voted_for_id')
.joins('INNER JOIN season_rosters ON season_rosters.season_id = V.season_id')
.where('V.is_jury_vote = (?) AND V.contestant_id <> (?) AND XV.tribal_council_key IS NOT NULL', :false, self.id)
.order('season_rosters.finished')
#.distinct
The error I get is below...
TinyTds::Error: Incorrect syntax near '*'.: EXEC sp_executesql N'SELECT DISTINCT *, __order FROM ( SELECT [contestants].*, DENSE_RANK() OVER (ORDER BY season_rosters.finished ASC) AS __order, ROW_NUMBER() OVER (PARTITION BY [contestants].* ORDER BY season_rosters.finished ASC) AS __joined_row_num FROM [contestants] INNER JOIN votes AS V ON V.contestant_id = contestants.id AND V.season_id = 6 LEFT OUTER JOIN votes AS XV ON (XV.contestant_id = 112) AND (XV.tribal_council_key = V.tribal_council_key) AND XV.contestant_voted_for_id = V.contestant_voted_for_id INNER JOIN season_rosters ON season_rosters.season_id = V.season_id WHERE (V.is_jury_vote = (''false'') AND V.contestant_id <> (112) AND XV.tribal_council_key IS NOT NULL) ) AS __sq WHERE __joined_row_num = 1 ORDER BY __order'
The issue is with this part:
SELECT DISTINCT *, __order
Try adding the required columns to your GROUP BY.
Contestant.joins('INNER JOIN votes AS V ON V.contestant_id = contestants.id AND V.season_id = '+ season_number.to_s)
.joins('LEFT OUTER JOIN votes AS XV ON (XV.contestant_id = '+self.id.to_s+') AND (XV.tribal_council_key = V.tribal_council_key) AND XV.contestant_voted_for_id = V.contestant_voted_for_id')
.joins('INNER JOIN season_rosters ON season_rosters.season_id = V.season_id')
.where('V.is_jury_vote = (?) AND V.contestant_id <> (?) AND XV.tribal_council_key IS NOT NULL', :false, self.id)
.order('season_rosters.finished')
.group('col1,col2,__order')
Also in your SQL error, order by is on a different column while in your code, it is on season_rosters.finished.

Export products from Mysql Magento

I have some problems with Magento DB, i cannot match the attributes for my custom products export file.
I need help to get attributes, i have finished this script only with this header
But attribute_set, visibility and size_cloth is returning number not name. maybe this can be resolved with a match function.
I need to get more attributes, ex: category_ids, i cannot get this...
color, size_shoes, size_etc.
Please check my script.
'store', '_website', 'attribute_set', 'type', 'sku', 'name', 'description', 'short_description', 'visibility', 'has_option', 'price', 'special_price', 'size_cloth', 'link', 'image'
<?php
//Setup Connection information
$dbhost = 'localhost';
$dbuser = 'user';
$dbpass = 'pass';
//Connect to the database
$conn = mysql_connect($dbhost, $dbuser, $dbpass) or die('Error connecting to mysql');
//Point to specific DB
$dbname = 'database_name';
mysql_select_db($dbname);
//Create the Query to get the products
$sql = "SELECT DISTINCT P.attribute_set_id, P.type_id, P.sku, P.has_options, V.value AS Name, T1.value AS ProdDesc, T2.value AS ShortDesc,
T5.value AS visibility, D.value AS Price, S.value AS Special_Price, SIZE_CLOTH.value AS size_cloth, CONCAT('http://www.website.com/', V1.value) AS Link,
CASE
WHEN V2.Value IS NULL
THEN NULL
ELSE CONCAT('http://www.website.com/media/catalog/product', V2.value)
END AS Image
FROM catalog_product_entity AS P INNER JOIN
catalog_product_entity_varchar AS V ON P.entity_id = V.entity_id AND V.attribute_id = 60 LEFT JOIN
catalog_product_entity_varchar AS V1 ON P.entity_id = V1.entity_id AND V1.attribute_id = 87 LEFT JOIN
catalog_product_entity_varchar AS V2 ON P.entity_id = V2.entity_id AND V2.attribute_id = 74 LEFT JOIN
catalog_product_entity_text AS T1 ON P.entity_id = T1.entity_id AND T1.attribute_id = 61 LEFT JOIN
catalog_product_entity_text AS T2 ON P.entity_id = T2.entity_id AND T2.attribute_id = 62 LEFT JOIN
catalog_product_entity_int AS T5 ON P.entity_id = T5.entity_id AND T5.attribute_id = 91 LEFT JOIN
catalog_product_entity_decimal AS D ON P.entity_id = D.entity_id AND D.attribute_id = 64 LEFT JOIN
catalog_product_entity_int AS SIZE_CLOTH ON P.entity_id = SIZE_CLOTH.entity_id AND SIZE_CLOTH.attribute_id = 122 LEFT JOIN
catalog_product_entity_decimal AS S ON P.entity_id = S.entity_id AND S.attribute_id = 65";
//Run the query
$query = mysql_query($sql);
//But after this i will prepare the csv file for export.
$file = fopen('products_export.csv', 'w');
// Custom header for csv file
$header = array('store', '_website', 'attribute_set', 'type', 'sku', 'name', 'description', 'short_description', 'visibility', 'has_option', 'price', 'special_price', 'size_cloth', 'link', 'image');
// this will arrange all data by comma
fputcsv($file, $header, ',', '"');
//Loop through and print each products info
while($row = mysql_fetch_array($query))
{
$item = array();
$value1 = ("admin");
$value2 = ("base");
$value3 = ($row['attribute_set_id']);
$value4 = ($row['type_id']);
$value5 = ($row['sku']);
$value6 = ($row['Name']);
$value7 = ($row['ProdDesc']);
$value8 = ($row['ShortDesc']);
$value9 = ($row['visibility']);
$value10 = ($row['has_options']);
$value11 = ($row['Price']);
$value111 = ($row['Special_Price']);
$value112 = ($row['size_cloth']);
$value12 = ($row['Link']);
//$value13 = ($row['Image']);
$item[] = $value1;
$item[] = $value2;
$item[] = $value3;
$item[] = $value4;
$item[] = $value5;
$item[] = $value6;
$item[] = $value7;
$item[] = $value8;
$item[] = $value9;
$item[] = $value10;
$item[] = $value11;
$item[] = $value111;
$item[] = $value112;
$item[] = $value12;
// put all data in csv file
fputcsv($file, $item, ',', '"');
}
{
// close csv file and finish.
fclose($file);
}
?>
I think that the best solution will be the data.
Example:
if ( $value112 == need to array the list of numbers, 1, 2, 3, 4, 5) {
echo "S, M, L, XL";
} else {
echo " ";
}
But i dont know how to insert this to my array items...
Interacting directly with Magento's DB wouldn't be my 1st choice.
Alternatives that actually work:
Use Magento Import/Export module
Use Magento Dataflow Import/Export
Use Magento's product collection to generate the export file
http://www.magentocommerce.com/knowledge-base/entry/magento-for-dev-part-8-varien-data-collections

Resources