How to use group by in Informix? - informix

I'm going to use the group by clause...max().
I'm using Informix version 10.
This is example table: table_a
col_a col_b col_c
1 20181010 3
1 20181030 4
I want to retrieve data with a recent date.
I want result :
col_a col_b col_c
1 20181030 4
When I use this query
#query 1: select col_a, max(col_b), col_c from table_a group by col_a
#result : The column (col_c) must be in the GROUP BY list.
#query 2: select col_a, max(col_b), col_c from table_a group by col_a, col_c
#result :
col_a col_b col_c
1 20181010 3
1 20181030 4
I think can I use MS SQL of row_num(partition by col_b)? but
Informix version 10 can't use row_num...
So, I use a join query
select a.col_a,a.col_b,a.col_c from table_a a
inner join (select col_a, max(col_b) as col_b from table_a group by col_a) b
on a.col_a = b.col_a and a.col_b = b.col_b
I got the results I wanted.
Is there a way to use join?

Related

Find n most referenced records by foreign_key in related table

I have a table skills and a table programs_skills which references skill_id as a foreign key, I want to retrieve the 10 most present skills in table programs_skills (I need to count the number of occurrence of skill_id in programs_skills and then order it by descending order).
I wrote this in my skill model:
def self.most_used(limit)
Skill.find(
ActiveRecord::Base.connection.execute(
'SELECT programs_skills.skill_id, count(*) FROM programs_skills GROUP BY skill_id ORDER BY count DESC'
).to_a.first(limit).map { |record| record['skill_id'] }
)
end
This is working but I would like to find a way to perform this query in a more elegant, performant, "activerecord like" way.
Could you help me rewrite this query ?
Just replace your query by:
WITH
T AS
(
SELECT skill_id, COUNT(*) AS NB, RANK() OVER(ORDER BY COUNT(*) DESC) AS RNK
FROM programs_skills
GROUP BY skill_id
)
SELECT wojewodztwo, NB
FROM T
WHERE RNK <= 10
This use CTE and windowed function.
ProgramsSkills.select("skill_id, COUNT(*) AS nb_skills")
.group(:skill_id).order("nb_skills DESC").limit(limit)
.first(limit).pluck(:skill_id)

Select rows with no match in join table with where condition

In a Rails app with Postgres I have a users, jobs and followers join table. I want to select jobs that are not followed by a specific user. But also jobs with no rows in the join table.
Tables:
users:
id: bigint (pk)
jobs:
id: bigint (pk)
followings:
id: bigint (pk)
job_id: bigint (fk)
user_id: bigint (fk)
Data:
sandbox_development=# SELECT id FROM jobs;
id
----
1
2
3
(3 rows)
sandbox_development=# SELECT id FROM users;
id
----
1
2
sandbox_development=#
SELECT id, user_id, job_id FROM followings;
id | user_id | job_id
----+---------+--------
1 | 1 | 1
2 | 2 | 2
(2 rows)
Expected result
# jobs
id
----
2
3
(2 rows)
Can I create a join query that is the equivalent of this?
sandbox_development=#
SELECT j.id FROM jobs j
WHERE NOT EXISTS(
SELECT 1 FROM followings f
WHERE f.user_id = 1 AND f.job_id = j.id
);
id
----
2
3
(2 rows)
Which does the job but is a PITA to create with ActiveRecord.
So far I have:
Job.joins(:followings).where(followings: { user_id: 1 })
SELECT "jobs".* FROM "jobs"
INNER JOIN "followings"
ON "followings"."job_id" = "jobs"."id"
WHERE "followings"."user_id" != 1
But since its an inner join it does not include jobs with no followers (job id 3). I have also tried various attempts at outer joins that either give all the rows or no rows.
In Rails 5, You can use #left_outer_joins with where not to achieve the result. Left joins doesn't return null rows. So, We need to add nil conditions to fetch the rows.
Rails 5 Query:
Job.left_outer_joins(:followings).where.not(followings: {user_id: 1}).or(Job.left_outer_joins(:followings).where(followings: {user_id: nil}))
Alternate Query:
Job.left_outer_joins(:followings).where("followings.user_id != 1 OR followings.user_id is NULL")
Postgres Query:
SELECT "jobs".* FROM "jobs" LEFT OUTER JOIN "followings" ON "followings"."job_id" = "jobs"."id" WHERE "followings"."user_id" != 1 OR followings.user_id is NULL;
I'm not sure I understand, but this has the output you want and use outer join:
SELECT j.*
FROM jobs j LEFT JOIN followings f ON f.job_id = j.id
LEFT JOIN users u ON u.id = f.user_id AND u.id = 1
WHERE u.id IS NULL;

How to write the below SQL query in rails 3 ActiveRecord?

select * from
(
SELECT DISTINCT ON (table1.id) table1.*, table3.date_filed as date_filed
FROM
table1 LEFT JOIN table2 ON table2.id = table1.some_id
INNER JOIN table3 ON table2.id = table3.some_id
WHERE
(
status IN('Supervisor Accepted')
)
AND(table3.is_main)
)first_result
ORDER BY date_filed ASC LIMIT 25 OFFSET 0
Is there any way to run main/subset query in the database side through Active::record (Rails 3). I don't want run the first_result(First db query) and the order by on the top of the result(Second db query).
I tried the below:
# First query run
first_result = Table1.select('DISTINCT ON (table1.id) table1.*, table3.date_filed').
joins('LEFT JOIN table2 ON table2.id = table1.some_id'). # I don't want a association here
joins('INNER JOIN table3 ON table2.id = table3.some_id').
where('table3.is_main')
# Second query run, WHICH is UGLY and not working properly
Table1.where(id: first_result.collect(:&id)).
order_by('date_filed ASC')
page(page).
per_page(per_page)

Arel count on union results

I have two tables ITEMS and ITEM_AUDITS. An item can have 0 or more audit records associated to it. Resource and status are required for fields and lookups.
I am trying to create a query that counts the number of occurrences of the item_id in both tables. Effectively producing data that looks like the example below
Title Item_ID Count
ABC 1 2
ABC 3 4
Bible 5 1
I have been able to create a union query that produces the data without the counts:
# Declare Arel objects
i = Item.arel_table
ia = ItemAudit.arel_table
s = Status.arel_table
r = Resource.arel_table
######################
# Build the item query
######################
# Build the joins
item = Item.joins(
i.join(r, Arel::Nodes::OuterJoin).on(i[:resource_id].eq(r[:id]))
.join(s, Arel::Nodes::OuterJoin).on(i[:status_id].eq(s[:id]))
.join_sql
).uniq
# Build the select columns
#item = item.select('resources.title, items.id as id, count(*) as loan_count')
item = item.select('resources.title, items.id as item_id')
# Adds the where criteria
item = item.where(
s[:title].matches("On Loan").or(s[:title].matches("Permanent Loan"))
)
# Add the group by clause
item = item.group("items.id")
##########################
# Build item history query
##########################
# Build the joins
item_audit = Item.joins(
i.join(ia).on(i[:id].eq(ia[:item_id]))
.join(r, Arel::Nodes::OuterJoin).on(i[:resource_id].eq(r[:id]))
.join(s, Arel::Nodes::OuterJoin).on(i[:status_id].eq(s[:id]))
.join_sql
)
# Build the select columns
item_audit = item_audit.select('resources.title, item_audits.item_id as item_id')
#######################
# Union the two queries
#######################
report = item.union(:all, item_audit).select('title, item_id')
I can't progress past this to get the counts which should be something like
report = item.union(:all, item_audit).select('title, item_id, count(item_id)')
EDIT
The SQL I am trying to end up with is as below (a simple group and count on the union results).
select qry.title, qry.item_id, count(qry.item_id) from(
SELECT DISTINCT
resources.title, items.id as item_id
FROM
`items`
LEFT OUTER JOIN
`resources` ON `items`.`resource_id` = `resources`.`id`
LEFT OUTER JOIN
`statuses` ON `items`.`status_id` = `statuses`.`id`
WHERE
((`statuses`.`title` LIKE 'On Loan'
OR `statuses`.`title` LIKE 'Permanent Loan'))
GROUP BY items.id
UNION ALL SELECT
resources.title, item_audits.item_id as item_id
FROM
`items`
INNER JOIN
`item_audits` ON `items`.`id` = `item_audits`.`item_id`
LEFT OUTER JOIN
`resources` ON `items`.`resource_id` = `resources`.`id`
LEFT OUTER JOIN
`statuses` ON `items`.`status_id` = `statuses`.`id`) as qry
group by qry.item_id
Anyone give me any pointers.
Many thanks,

Nested query in squeel

Short version: How do I write this query in squeel?
SELECT OneTable.*, my_count
FROM OneTable JOIN (
SELECT DISTINCT one_id, count(*) AS my_count
FROM AnotherTable
GROUP BY one_id
) counts
ON OneTable.id=counts.one_id
Long version: rocket_tag is a gem that adds simple tagging to models. It adds a method tagged_with. Supposing my model is User, with an id and name, I could invoke User.tagged_with ['admin','sales']. Internally it uses this squeel code:
select{count(~id).as(tags_count)}
.select("#{self.table_name}.*").
joins{tags}.
where{tags.name.in(my{tags_list})}.
group{~id}
Which generates this query:
SELECT count(users.id) AS tags_count, users.*
FROM users INNER JOIN taggings
ON taggings.taggable_id = users.id
AND taggings.taggable_type = 'User'
INNER JOIN tags
ON tags.id = taggings.tag_id
WHERE tags.name IN ('admin','sales')
GROUP BY users.id
Some RDBMSs are happy with this, but postgres complains:
ERROR: column "users.name" must appear in the GROUP BY
clause or be used in an aggregate function
I believe a more agreeable way to write the query would be:
SELECT users.*, tags_count FROM users INNER JOIN (
SELECT DISTINCT taggable_id, count(*) AS tags_count
FROM taggings INNER JOIN tags
ON tags.id = taggings.tag_id
WHERE tags.name IN ('admin','sales')
GROUP BY taggable_id
) tag_counts
ON users.id = tag_counts.taggable_id
Is there any way to express this using squeel?
I wouldn't know about Squeel, but the error you see could be fixed by upgrading PostgreSQL.
Some RDBMSs are happy with this, but postgres complains:
ERROR: column "users.name" must appear in the GROUP BY clause or be
used in an aggregate function
Starting with PostgreSQL 9.1, once you list a primary key in the GROUP BY you can skip additional columns for this table and still use them in the SELECT list. The release notes for version 9.1 tell us:
Allow non-GROUP BY columns in the query target list when the primary
key is specified in the GROUP BY clause
BTW, your alternative query can be simplified, an additional DISTINCT would be redundant.
SELECT o.*, c.my_count
FROM onetable o
JOIN (
SELECT one_id, count(*) AS my_count
FROM anothertable
GROUP BY one_id
) c ON o.id = counts.one_id

Resources