I have a table with this structure:
id | name | batch_id | used
----------------------------------
1 | voucher 1 | 1 | 1
2 | voucher 2 | 1 | 0
3 | voucher 3 | 1 | 1
4 | voucher 4 | 2 | 0
5 | voucher 5 | 3 | 1
And I need to run a query that will group by batch_id and do a sum on the used column.
So the outcome that I need is:
batch_id | sum_used
----------------------------------
1 | 2
2 | 0
3 | 1
This is what I have so far:
TableName.select('DISTINCT ON (batch_id) batch_id')
Which is the same as:
SELECT DISTINCT ON (batch_id) batch_id FROM table_name
Grouping works but I can't get the sum to work.
Can anyone point me in the right direction?
You need to use GROUP BY and SUM aggregation function:
SELECT batch_id, SUM(used::int) AS sum_used
FROM table_name
GROUP BY batch_id
ORDER BY batch_id;
SqlFiddleDemo
Output:
╔═══════════╦══════════╗
║ batch_id ║ sum_used ║
╠═══════════╬══════════╣
║ 1 ║ 2 ║
║ 2 ║ 0 ║
║ 3 ║ 1 ║
╚═══════════╩══════════╝
Alternatively using windowed function:
SELECT DISTINCT batch_id, SUM(used::int) OVER(PARTITION BY batch_id) AS sum_used
FROM table_name
ORDER BY batch_id
SqlFiddleDemo2
Hey you can try this way:
TableName.select('batch_id as batch_id , sum(used) as sum_used').group(:batch_id)
Related
just discovered that my source data is being updated from a third-party source which I can't change. As such, my orders file actually has all order history including updates of quantity, etc.
I am trying to create a sheet that pulls ONLY the summary values for the most recent version of the order. The example below is an actual extract from the data set - without all the extra data. As you can see, Bill's order was updated three times before it shipped.
I need to group on Order Number and return ONLY the last update from 09/08/2021.
There are obviously many rows (17,000) to be exact with approximately 8000 orders. About 10% of the orders are updated like this. Does anyone have any suggestions for grouping and reporting on the Latest date?
A B C D E
Order Number | Name | Item | QTY | Updated
1001 | Bill | ABC | 10 | 30/07/2021
1001 | Bill | DEF | 5 | 30/07/2021
1001 | Bill | GHI | 5 | 30/07/2021
1001 | Bill | ABC | 10 | 07/08/2021
1001 | Bill | DEF | 5 | 07/08/2021
1001 | Bill | GHI | 7 | 07/08/2021
1001 | Bill | ABC | 2 | 09/08/2021
1001 | Bill | DEF | 4 | 09/08/2021
1001 | Bill | GHI | 2 | 09/08/2021
I want to pull a query back with this group by order number for the last update and sum the QTY.
For this subset of data, the result should look like this.
1001 | Bill | 8 | 09/08/2021
=query(Orders!A1:E,"Select A, B, Sum(D), E group by A, B, E Where E = date ‘”&Text(Max(Orders!E:E),"YYY-MM-DD”)&”‘”,1)
I am getting an error. Any idea? Thanks
try:
=QUERY(Orders!A1:E,
"select A,B,sum(D),max(E)
Where E = date '"&TEXT(MAX(Orders!E:E), "YYYY-MM-DD")&"'
group by A,B", 1)
In PSQL I am aggregating concatenated strings from a table called genus_synonym
An example of the table is as follows
id|genus_synonym|specific_epithet_synonym
---|----------|-----------
1 | Acer | rubrum
2 | Acer | nigrum
3 | Betula | lenta
4 | Carya | ovata
5 | Carya | glabra
6 | Carya | tomentosa
here is an image of my table if that is easier
the code I am using is like this
Select
string_agg(CONCAT(CONCAT(s."genus_synonym"), ' ', s.specific_epithet_synonym), ', ')as syno
FROM
"public"."synonyms" as s
The result is:
Acer rubrum, Acer nigrum, Betula lenta, Carya ovata, Carya glabra, Carya tomentosa
What I am trying to figure out is if it is possible to instead produce this:
Acer rubrum, A. nigrum, Betula lenta, Carya ovata, C. glabra, C. tomentosa
Basically I am wanting to abbreviate the genus name to a single letter with a period following it, for the second and additional time a genus is repeated.
Even if this is not possible it would be good to know this and then if there was another way I could go about solving this.
Also, it doesn't look like anyone is responding to my question. Is it not clear? I haven't been able to find anything like this being asked before. Please let me know what I can do to make this question better.
qry:
t=# with a as (
select *,case when row_number() over (partition by genus_synonym) > 1 and count(1) over (partition by genus_synonym) > 1 then substr(genus_synonym,1,1)||'.' else genus_synonym end sh
from s92
)
select string_agg(concat(sh,' ',specific_epithet_synonym),',')
from a;
string_agg
-----------------------------------------------------------------------
Acer rubrum,A. nigrum,Betula lenta,Carya ovata,C. glabra,C. tomentosa
(1 row)
Time: 0.353 ms
mockup your data:
t=# create table s92 (id int,genus_synonym text,specific_epithet_synonym text);
CREATE TABLE
Time: 7.587 ms
t=# copy s92 from stdin delimiter '|';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>> 1 | Acer | rubrum
2 | Acer | nigrum
3 | Betula | lenta
4 | Carya | ovata
5 | Carya | glabra
6 | Carya | tomentosa
>> >> >> >> >> >> \.
COPY 6
Time: 6308.728 ms
I have three tables
Personal_video
+------------------------------+
|presonal_video_id | title |
----------------------------
1 | test1|
2 | test2|
3 | test3|
4 | test4|
personal_video_tags
+------------------------------+
|tag_id | tag_title |
----------------------------
1 | august|
2 | 2016 |
3 | 2015 |
4 | 2014 |
personal_video_tag_mapping
+------------------------------+
|tag_id | presonal_video_id |
----------------------------
1 | 1 |
2 | 2 |
3 | 3 |
4 | 1 |
Now i want to write a query which will return me the videos on the basis of common tags like if user select tag "August" & "2014" then the query should return videos which is connected to both the tags.
currently my query is
SELECT presonal_video_id,title
FROM personal_video
WHERE presonal_video_id IN
(
SELECT personal_video_id AS PID
FROM personal_video_tag_mapping
WHERE tag_id IN ("1","2") AND privacy_level != 2
GROUP BY personal_video_id
HAVING COUNT( PID ) > 1
)
It is giving me write result but when there is large data then it takes long time. Can someone teel me correct way to write this query
Thank You in advance
Try this query:
SELECT t1.presonal_video_id, t1.title
FROM personal_video AS t1
JOIN personal_video_tag_mapping AS t2
ON t1.presonal_video_id = t2.presonal_video_id
JOIN personal_video_tags AS t3
ON t2.tag_id = t3.tag_id
WHERE t3.tag_title IN ('august', '2014')
GROUP BY t1.presonal_video_id, t1.title
HAVING COUNT(*) = 2
Im trying to find an efficient way to solve the problem:
I need to find all rows in a table where there is another row with an opposite column value.
For example I have transactions with columns id and amount
| id | amount |
|----|--------|
| 1 | 1 |
| 2 | -1 |
| 3 | 2 |
| 4 | -2 |
| 5 | 3 |
| 6 | 4 |
| 7 | 5 |
| 8 | 6 |
The query should return only the first 4 rows:
| id | amount |
|----|--------|
| 1 | 1 |
| 2 | -1 |
| 3 | 2 |
| 4 | -2 |
My current solution is terribly efficient as I am going through 1000's of transactions:
transactions.find_each do |transaction|
unless transactions.where("amount = #{transaction.amount * -1}").count > 0
transactions = transactions.where.not(amount: transaction.amount).order("# amount DESC")
end
end
transactions
Are there any built in Rails or Postgresql functions that could help with this?
Use following query:
SELECT DISTINCT t1.*
FROM transactions t1
INNER JOIN transactions t2 ON t1.amount = t2.amount * -1;
SELECT * FROM the_table t
WHERE EXISTS (
SELECT * FROM the_table x
WHERE x.amount = -1*t.amount
-- AND x.amount > t.amount
);
Consider storing an absolute value indexed column then query for the positive value. Postgres has an absolute value function; but I think the beauty of ActiveRecord is that Arel abstracts away the SQL. DB specific SQL can be a pain if you change later.
There is type called abs which will return irrespective of symobol. From my example data is the table name
SELECT id,amount FROM DATA WHERE id = ABS(amount)
This is the sample test table
Here is the output
I have a number of locations with people that are at various steps in a step-wise process. I'd like to be able to report the count of people at each step by location and then the total for all locations. So my data looks like this (steps table)
| ID | STEP_NUM | LOCATION ID |
-------------------------------
| 1 | 1 | 10 |
| 2 | 1 | 12 |
| 3 | 2 | 4 |
| 4 | 1 | 5 |
| 5 | 1 | 6 |
| 6 | 1 | 3 |
| 7 | 3 | 3 |
This stackoverflow question and answer(s) Postgresql Multiple counts for one table was very useful and I got the summary by location. Here is my current query:
SELECT locations.name,
sum(case when step_num = 1 then 1 end) as Beginner,
sum(case when step_num = 2 then 1 end) as Intermediate,
sum(case when step_num = 3 then 1 end) as Expert
FROM steps
INNER JOIN locations ON steps.location_id = locations.id
GROUP BY locations.name
ORDER BY locations.name ASC
How would I also return the total for all locations? For example I would like to get the result:
| LOCATION NAME | Beginner | Intermediate | Expert |
----------------------------------------------------
| Uptown | 5 | | 1 |
| Downtown | 2 | 1 | 3 |
| All locations | 7 | 1 | 4 |
You need rollup operation which is not supported in PostgreSQL yet but can be emulated
WITH location AS (
SELECT locations.name,
sum(case when step_num = 1 then 1 end) as Beginner,
sum(case when step_num = 2 then 1 end) as Intermediate,
sum(case when step_num = 3 then 1 end) as Expert
FROM steps
INNER JOIN locations ON steps.location_id = locations.id
GROUP BY locations.name
ORDER BY locations.name ASC
), total AS (
SELECT 'Total',
sum(Beginner),
sum(Intermediate),
sum(Expert)
FROM location
) SELECT *
FROM location
UNION ALL
SELECT *
FROM total