MS Access Group Specific Values Together - sql

So I am working on a problem in MS Access where I need some aggregated values grouped by certain values.
ExTable1:
Type TotalHours
+-------+------------+
| A 10 |
| A 20 |
| A 30 |
| B 10 |
| C 10 |
| D 10 |
| E 10 |
| F 10 |
+-------+------------+
And I have this query:
SELECT Type, SUM(TotalHours)
FROM ExTable1
GROUP BY Type
This groups all of the A's, B's, C's, etc.. together, but I also want to group E and F's values together in the same query. How would I do this?

Consider using a Union Query:
SELECT Type, Sum(TotalHours) As TotalHours
FROM ExTable1
WHERE Type IN ('A', 'B', 'C', 'D')
GROUP BY Type;
UNION SELECT 'E & F', Sum(TotalHours)
FROM ExTable1
WHERE Type IN ('E', 'F');

Use Conditional Aggregate
SELECT Type,
SUM(TotalHours),
SUM(case when Type in ('E','F') then TotalHours else 0 END) as 'E&F total'
FROM ExTable1
GROUP BY Type

Related

Postgresql - multiple select condition

I have table with following structure:
|id|author_id|name|type|created_at|updated_at
As a type I can have 5 different types, (A, B, C, D, E).
I need a query DB by author_id where I can select only last updated row "type" A and B. And select all other type rows.
So the result should be something like:
| id | author_id | name | type | created_at | updated_at
| 12 | 88 | lorem | A
| 45 | 88 | lorem | B
| 44 | 88 | lorem | C
| 154 | 88 | lorem | C
| 98 | 88 | lorem | C
| 856 | 88 | lorem | E
| 857 | 88 | lorem | E
Is it possible with single query? Or I need to use two queries?
Thank you
You may try the following:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY "type" ORDER BY updated_at DESC) rn
FROM yourTable
)
SELECT id, author_id, name, "type", created_at, updated_at
FROM cte
WHERE
("type" IN ('A', 'B') AND rn = 1) OR
"type" NOT IN ('A', 'B');
This approach uses ROW_NUMBER to find the latest rows for all types. In the query on the CTE, we select only the most recently updated rows for types A and B, but we select all rows for all other types.
Assuming that id is a unique key in the table, you could do this with distinct on:
select distinct on(case when type in ('A', 'B') then type else id::text end) t.*
from mytable t
order by case when type in ('A', 'B') then type else id::text end, created_at desc, id
This uses a conditional expression as distinct on key, that returns either type if it is A or B, or the id for other values. So you get the top 1 value for types A and B, and all other values for other types.
I am thinking:
(select distinct on (type) t.*
from t
where type in ('A', 'B')
order by type, created_at desc
) union all
select t.*
from t
where type not in ('A', 'B');
In particular, this can make good use of an index on (type, created_at desc).

Checking using nvl2 with multiple group by

I have a table like
------------------------
S.No Name Amount Imp_Num
1 A 10 12345
2 B 20
3 A 30
4 C 40 4555
5 B 50
--------------------------
and I want something like
---------------------------------------
Name Total_Amount Imp_Num Imp_Num_Present
A 40 12345 Y
B 70 null N
C 40 4555 Y
---------------------------------------
The important_number_present column should be Y if the important number is present for the particular name at least once and the important number should be captured. The important number for a particular name is assumed to be the same.If different the latest one should be displayed as imp_numb. (But this is of secondary priority).
I tried something like
Select sum(amount) as total_amount, imp_num, nvl2(imp_num,'Y','N') from sampletable group by imp_num;
But name can't be retrieved and the data doesn't make sense without the name. I might be doing something totally wrong. Can a feasible solution be done in SQL rather than in pl/sql.
Group by with name is returning the name with a null entry and imp_num entry.
I am cracking my head on this. Would be of great help, if someone solves it.
Thanks in advance
You could use a (fake) aggregation function on imp_num and group by name
Select Name, sum(amount) as total_amount, max(imp_num), nvl2( max(imp_num),'Y','N')
from sampletable
group by Name;
EDIT: Another solution with COUNT function. DEMO
SELECT name
,SUM(amount) AS total_amount
,MAX(imp_num) AS Imp_Num
,CASE
WHEN Count(imp_num) > 0
THEN 'Y'
ELSE 'N'
END AS Imp_Num_Present
FROM yourtable
GROUP BY name
You may also use a MAX( CASE ) block
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE yourtable
(S_No int, Name varchar2(1), Amount int, Imp_Num varchar2(5))
;
INSERT ALL
INTO yourtable (S_No, Name, Amount, Imp_Num)
VALUES (1, 'A', 10, '12345')
INTO yourtable (S_No, Name, Amount, Imp_Num)
VALUES (2, 'B', 20, NULL)
INTO yourtable (S_No, Name, Amount, Imp_Num)
VALUES (3, 'A', 30, NULL)
INTO yourtable (S_No, Name, Amount, Imp_Num)
VALUES (4, 'C', 40, '4555')
INTO yourtable (S_No, Name, Amount, Imp_Num)
VALUES (5, 'B', 50, NULL)
SELECT * FROM dual
;
Query 1:
SELECT Name,
SUM (amount) AS total_amount,
MAX (imp_num) AS Imp_Num,
CASE
WHEN MAX (CASE WHEN imp_num IS NOT NULL THEN 1 ELSE 0 END) = 1
THEN
'Y'
ELSE
'N'
END
AS Imp_Num_Present
FROM yourtable
GROUP BY Name
Results:
| NAME | TOTAL_AMOUNT | IMP_NUM | IMP_NUM_PRESENT |
|------|--------------|---------|-----------------|
| A | 40 | 12345 | Y |
| B | 70 | (null) | N |
| C | 40 | 4555 | Y |

How to use SUM over GROUP BY in SQL Server?

I currently have:
SELECT Name, COUNT(*) as Total
FROM DataTable
WHERE Name IN ('A', 'B', 'C')
GROUP BY Name
Resulting output:
Name Total
--------------
A 2
B 5
C 3
Instead I want this:
Name Total
--------------
A 10
B 10
C 10
Here 10 is a total of 2 + 5 + 3 (total number of records with name = A/B/C)
How do I do this?
To get your desired result you can use SUM() OVER () on the grouped COUNT(*). Demo
SELECT Name,
SUM(COUNT(*)) OVER () as Total
FROM DataTable
WHERE Name IN ('A', 'B', 'C')
GROUP BY Name
Get rid of the group by and use distinct:
select distinct Name, count(*) over() as Total
from t
where name in ('A', 'B', 'C')
rextester demo: http://rextester.com/WDMT68119
returns:
+------+-------+
| name | Total |
+------+-------+
| A | 10 |
| B | 10 |
| C | 10 |
+------+-------+
If you count all of the records and then do a cross join on all the different names
SELECT a.NAME
,x.Total
FROM DataTable a
CROSS JOIN (
COUNT(*) AS Total FROM DataTable
) x
GROUP BY a.NAME
,x.Total

Select rows with highest value in one column in SQL

in MySQL, I am trying to select one row for each "foreign_id". It must be the row with the highest value in column "time" (which is of type DATETIME). Can you help me how the SQL SELECT statement must look like? Thank you!
This would be really great! I am already trying for hours to find a solution :(
This is my table:
primary_id | foreign_id | name | time
----------------------------------------------------
1 | 3 | a | 2017-05-18 01:02:03
2 | 3 | b | 2017-05-19 01:02:03
3 | 3 | c | 2017-05-20 01:02:03
4 | 5 | d | 2017-07-18 01:02:03
5 | 5 | e | 2017-07-20 01:02:03
6 | 5 | f | 2017-07-18 01:02:03
And this is what the result should look like:
primary_id | foreign_id | name | time
----------------------------------------------------
3 | 3 | c | 2017-05-20 01:02:03
5 | 5 | e | 2017-07-20 01:02:03
I tried to order the intermediate result by time (descending) and then to select only the first row by using LIMIT 1. But like this I cannot get one row for each foreign_id.
Another try was to first order the intermediate result by time (descending) and then to GROUP BY foreign_id. But the GROUP BY statement seems to be executed before the ORDER BY statement (I received the rows with primary_id 1 and 4 as a result, not 3 and 5).
Try this
SELECT DISTINCT *
From my_table A
INNER JOIN (SELECT foreign_id, Max(time) AS time FROM my_table GROUP BY foreign_id) B
ON A.foreign_id = B.foreign_id AND A.time = B.time
Just add some data sample to analyze special case
CREATE TABLE Table1
(`primary_id` int, `foreign_id` int, `name` varchar(1), `time` datetime)
;
INSERT INTO Table1
(`primary_id`, `foreign_id`, `name`, `time`)
VALUES
(1, 3, 'a', '2017-05-18 01:02:03'),
(2, 3, 'b', '2017-05-19 01:02:03'),
(3, 3, 'c', '2017-05-20 01:02:03'),
(7, 3, 'H', '2017-05-20 01:02:03'),
(4, 5, 'd', '2017-07-18 01:02:03'),
(5, 5, 'e', '2017-07-20 01:02:03'),
(6, 5, 'f', '2017-07-18 01:02:03')
;
http://sqlfiddle.com/#!9/38947b/6
select d.primary_id, d.foreign_id, c.name, d.time
from table1 c inner join (
select max(b.primary_id) primary_id, a.foreign_id, a.time
from table1 b inner join
( select foreign_id, max(time) time
from table1
group by foreign_id) a
on a.foreign_id = b.foreign_id and a.time=b.time
group by a.foreign_id, a.time ) d
on c.primary_id=d.primary_id
In days gone by you would code this as a correlated subquery:
SELECT *
FROM Table1 o
WHERE primary_id = (
SELECT min (m.primary_id) FROM Table1 m
WHERE m.time= (
SELECT max (i.time) FROM Table1 i
WHERE o.foreign_id=i.foreign_id
)
)
The extra subquery handles the case of duplicate foreign_id & time values. If you were sure that time was unique for each foreign_id you could omit the middle subquery.

Need some sort of "conditional grouping" in MySQL

I have Article table:
id | type | date
-----------------------
1 | A | 2010-01-01
2 | A | 2010-01-01
3 | B | 2010-01-01
Field type can be A, B or C.
I need to run a report that would return how many articles of each type there is per every day, like this:
date | count(type="A") | count(type="B") | count(type="C")
-----------------------------------------------------
2010-01-01 | 2 | 1 | 0
2010-01-02 | 5 | 6 | 7
Currently I am running 3 queries for every type and then manually merging the results
select date, count(id) from article where type="A" group by date
Is it possible to do this in one query? (in pure sql, no stored procedures or anything like that).
Thanks
A combination of SUM and CASE should do ya
select date
, sum(case when type ='A' then 1 else 0 end) as count_type_a
, sum(case when type ='B' then 1 else 0 end) as count_type_b
, sum(case when type ='C' then 1 else 0 end) as count_type_c
from article group by date
EDIT: Alex's answer above uses a better approach that the one in this answer. I'm leaving it here just because it also satisfies the question, in an alternative way:
You should be able to use sub queries, as follows:
SELECT DATE(a.date) as date,
(SELECT COUNT(a1.id) FROM articles a1 WHERE a1.type = 'A' AND a1.date = a.date) count_a,
(SELECT COUNT(a2.id) FROM articles a2 WHERE a2.type = 'B' AND a2.date = a.date) count_b,
(SELECT COUNT(a3.id) FROM articles a3 WHERE a3.type = 'C' AND a3.date = a.date) count_c
FROM articles a
GROUP BY a.date;
Test Case:
CREATE TABLE articles (id int, type char(1), date datetime);
INSERT INTO articles VALUES (1, 'A', '2010-01-01');
INSERT INTO articles VALUES (2, 'A', '2010-01-01');
INSERT INTO articles VALUES (3, 'B', '2010-01-01');
INSERT INTO articles VALUES (4, 'B', '2010-01-02');
INSERT INTO articles VALUES (5, 'B', '2010-01-02');
INSERT INTO articles VALUES (6, 'B', '2010-01-03');
INSERT INTO articles VALUES (7, 'B', '2010-01-01');
INSERT INTO articles VALUES (8, 'C', '2010-01-05');
Result:
+------------+---------+---------+---------+
| date | count_a | count_b | count_c |
+------------+---------+---------+---------+
| 2010-01-01 | 2 | 2 | 0 |
| 2010-01-02 | 0 | 2 | 0 |
| 2010-01-03 | 0 | 1 | 0 |
| 2010-01-05 | 0 | 0 | 1 |
+------------+---------+---------+---------+
4 rows in set (0.00 sec)