Summarise Data based on an Account within a Query using SQL - sql

Come up against an issue where i want to summarize results in a query.
Example being as follows:
NAME | FRUIT | PRICE
JOHN | APPLE | 2
JOHN | APPLE | 2
JOHN | APPLE | 2
JOHN | APPLE | 2
DAVE | GRAPE | 3
DAVE | GRAPE | 3
DAVE | GRAPE | 3
This is my table at the moment, what i need though is to have a summary of Johns business, like below:
NAME | FRUIT | PRICE
JOHN | APPLE | 2
JOHN | APPLE | 2
JOHN | APPLE | 2
JOHN | APPLE | 2
JOHN | TOTAL | 8
DAVE | GRAPE | 3
DAVE | GRAPE | 3
DAVE | GRAPE | 3
I have tried to group the information but it does not reflect what i want, plus if John were to have different fruit it would need to sum that up before it sums up the next part.
Any advice would be great

Why do you want a summary only of John? You can use union all for this:
select name, fruit, price
from ((select name, fruit, price, 1 as ord, fruit as f
from t
) union all
(select name, 'Total', sum(price), 2 as ord, fruit as f
from t
where name = 'John'
group by name, fruit
)
) x
order by name, f, ord;

Related

Counting and presenting only duplicates

So I have a table like this:
Name | Fruit | price
----------------------
Effy | Apple| 8
Effy | Banana | 9
Alam | Apple | 8
Alam | Banana | 10
Boji | Apple | 11
Suppose I want to get rid only from the Names that has one value (like Boji). How can I do it in big Query?
I thought to add HAVING clause and to add Having count(*) > 2, since this table is already grouped by,
but i's no working the way I want.
Select Distinct name, fruit, price from fruit_db
group by name, fruit
output:
Name | Fruit | price
----------------------
Effy | Apple| 8
Effy | Banana | 9
Alam | Apple | 8
Alam | Banana | 10
Try below
select * except(qualified) from (
select *,
count(1) over(partition by name) > 1 qualified
from `project.dataset.table`
)
where qualified
if applied to sample data in your question - output is

FIRST & LAST values in Oracle SQL

I am having trouble querying some data. The table I am trying to pull the data from is a LOG table, where I would like to see changes in the values next to each other (example below)
Table:
+-----------+----+-------------+----------+------------+
| UNIQUE_ID | ID | NAME | CITY | DATE |
+-----------+----+-------------+----------+------------+
| xa220 | 1 | John Smith | Berlin | 2020.05.01 |
| xa195 | 1 | John Smith | Berlin | 2020.03.01 |
| xa111 | 1 | John Smith | München | 2020.01.01 |
| xa106 | 2 | James Brown | Atlanta | 2018.04.04 |
| xa100 | 2 | James Brown | Boston | 2017.12.10 |
| xa76 | 3 | Emily Wolf | Shanghai | 2016.11.03 |
| xa20 | 3 | Emily Wolf | Shanghai | 2016.07.03 |
| xa15 | 3 | Emily Wolf | Tokyo | 2014.02.22 |
| xa12 | 3 | Emily Wolf | null | 2014.02.22 |
+-----------+----+-------------+----------+------------+
Desired outcome:
+----+-------------+----------+---------------+
| ID | NAME | CITY | PREVIOUS_CITY |
+----+-------------+----------+---------------+
| 1 | John Smith | Berlin | München |
| 2 | James Brown | Atlanta | Boston |
| 3 | Emily Wolf | Shanghai | Tokyo |
| 3 | Emily Wolf | Tokyo | null |
+----+-------------+----------+---------------+
I have been trying to use FIRST and LAST values, however, cannot get the desired outcome.
select distinct id,
name,
city,
first_value(city) over (partition by id order by city) as previous_city
from test
Any help is appreciated!
Thank you!
Use the LAG function to get the city for previous date and display only the rows where current city and the result of lag are different:
WITH cte AS (
SELECT t.*, LAG(CITY, 1, CITY) OVER (PARTITION BY ID ORDER BY "DATE") LAG_CITY
FROM yourTable t
)
SELECT ID, NAME, CITY, LAG_CITY AS PREVIOUS_CITY
FROM cte
WHERE
CITY <> LAG_CITY OR
CITY IS NULL AND LAG_CITY IS NOT NULL OR
CITY IS NOT NULL AND LAG_CITY IS NULL
ORDER BY
ID, "DATE" DESC;
Demo
Some comments on how LAG is being used and its values checked are warranted. We use the three parameter version of LAG here. The second parameter means the number of records to look back, which in this case is 1 (the default). The third parameter means the default value to use should a given record per ID partition be the first. In this case, we use the default as the same CITY value. This means that the first record would never appear in the result set.
For the WHERE clause above, a matching record is one for which the city and lag city are different, or for where one of the two be NULL and the other not NULL. This is the logic needed to treat a NULL city and some not NULL city value as being different.

SQL Count of same country, same name, same age for people with same last name (e.g smith)

I need your advice.
we have the below table
Name of table: PEOPLE (i know bad name for example :-) )
ID | firstname | age | country
1 | George | 20 | US
1 | George | 20 | GB
2 | Jim | 20 | FR
2 | Jim | 21 | FR
i need to see the below result, so for same ID counts of values
ID | firstnamecnt | agecnt | countrycnt
1 | 2 | 2 | 1
2 | 2 | 1 | 2
I hope I explained well :-)
Thanks and regards,
Alex
I assume you meant firstname. Here is one way
select id,
count(*) - count(distinct firstname) + 1 as firstnamecnt,
count(*) - count(distinct age) + 1 as agecnt,
count(*) - count(distinct country) + 1 as countrycnt
from people
group by id;
DEMO

Joining multiple counts on the same table, from different columns?

I feel like this should be easy but it's late and I'm struggling.
Say (in an oracle 12 db) I have a table which represents which staff filled what roles in a bar, during different events, like this:
+----------+----------+-------+------------+----------+
| event_id | bar | doors | cloak_room | keg_room |
+----------+----------+-------+------------+----------+
| 2 | bob | bill | john | mary |
+----------+----------+-------+------------+----------+
| 3 | bob | bill | mary | kev |
+----------+----------+-------+------------+----------+
| 4 | bob | john | louise | mary |
+----------+----------+-------+------------+----------+
| 5 | kyle | kev | sarah | louise |
+----------+----------+-------+------------+----------+
| 6 | jennifer | bob | jay | john |
+----------+----------+-------+------------+----------+
| 7 | john | bill | mary | steve |
+----------+----------+-------+------------+----------+
and I want to get a count of, overall, how many events each staff member worked, like this:
+-------+--------+
| count | person |
+-------+--------+
| 4 | bob |
+-------+--------+
| 4 | john |
+-------+--------+
| 3 | bill |
+-------+--------+
| 3 | mary |
+-------+--------+
| 2 | kev |
+-------+--------+
| 2 | louise |
+-------+--------+
| 1 | jay |
+-------+--------+
| 1 | steve |
+-------+--------+
We see here that bob has a count of 4 - because he is associated with 4 distinct event_id: 3 as a barman, and 1 as a doorman.
(assuming no two staff members have the same name, and no one can work two jobs at once)
How do I do this?
for one 'role' it's clear:
select count(event_id), bar group by bar
but is there an elegant way to do this for all columns - without full joins and string concat?
thanks!
You should change the structure of your data, so you have one row per event/person/role. Then you could just use aggregation.
You can do that in a query as well:
select who, count(*)
from (select event_id, 'bar' as job, bar as who from t union all
select event_id, 'doors' as job, doors as who from t union all
select event_id, 'cloak_room' as job, cloak_room as who from t union all
select event_id, 'keg_room' as job, keg_room as who from t
) jw
group by who;
If someone could have multiple jobs in one event, then use count(distinct event_id).
EDIT:
I see you are using Oracle 12c. Then use a lateral join/cross apply:
select who, count(*)
from t cross apply
(select t.event_id, 'bar' as job, t.bar as who from dual union all
select t.event_id, 'doors' as job, t.doors as who from dual from dual union all
select event_id, 'cloak_room' as job, cloak_room as who from dual union all
select t.event_id, 'keg_room' as job, t.keg_room as who from dual
) jw
group by who;
You may count by string columns in the nested inner query and then sum them up outside with your desired order :
SELECT sum(count) count, person
FROM
(
SELECT count(event_id) count, bar person FROM mytable GROUP BY bar UNION ALL
--> P.S. Only aliasing as "person" is enough in this upper "select" for all
--> four "select" statements inside the parentheses.
SELECT count(event_id) , doors FROM mytable GROUP BY doors UNION ALL
SELECT count(event_id) , cloak_room FROM mytable GROUP BY cloak_room UNION ALL
SELECT count(event_id) , keg_room FROM mytable GROUP BY keg_room
)
GROUP BY person
ORDER BY 1 desc, 2;
COUNT PERSON
4 bob
4 john
3 bill
3 mary
2 kev
2 louise
1 jay
1 jennifer
1 kyle
1 mary2
1 sarah
1 steve
SQL Fiddle Demo

Rank by Agent and Month

I have a table with similar data to the following:
Agent | TCV | Parent AccountID | AccountID | Month
------+-----+------------------+-----------+--------
John | 100 | ABC12345 | Sept13445 | 2
John | 200 | ABC12345 | Sept345 | 2
John | 150 | CDE12345 | Sept546 | 2
John | 200 | FTE1456 | Oct3467 | 2
John | 100 | ABC12345 | Sept13445 | 3
John | 200 | ABC12345 | Sept345 | 3
John | 150 | CDE12345 | Sept546 | 3
John | 200 | FTE1456 | Oct3467 | 3
What I need is a way to show a grouped ranking by agent, then parent accounrDID each month for each agent. The idea being that in the export, there'd be a column for agent, TCV, parentaccountDID, and month.
So if all agents had 10 parentaccountDIDs (but potentially multiple accountDIDs under each), it would rank them by the grouped TCV of the parentAccountDID. So there'd be 10 lines of data for John's 10 parentaccountDIDs based off of grouped TCV in month 2, then 10 lines of data for the 10 parentaccountDIDs based off of grouped TCV in month 3, etc. etc.
SELECT agent, tcv, parent_accnt_id, accnt_id, curr_month
, ROW_NUMBER() OVER (PARTITION BY curr_month, parent_accnt_id ORDER BY curr_month) rnk
FROM your_table
/
With your data this is what you get:
AGENT TCV PARENT_ACCNT_ID ACCNT_ID CURR_MONTH RNK
------------------------------------------------------------------
John 100 ABC12345 Sept13445 2 1
John 200 ABC12345 Sept345 2 2
John 150 CDE12345 Sept546 2 1
John 200 FTE1456 Oct3467 2 1
John 100 ABC12345 Sept13445 3 1
John 200 ABC12345 Sept345 3 2
John 150 CDE12345 Sept546 3 1
John 200 FTE1456 Oct3467 3 1
In case you have Oracle SQL Database
Try the below query it might help you as needed:
select Agent ,TCV ,ParentAccountID,Month,count(1) from table group by Agent ,TCV ,ParentAccountID,Month
order by 5 desc
Also you could check(google) "Dense Rank Over" function something as below:
select Agent ,TCV ,ParentAccountID,Month,dense rank over(Agent ,TCV ,ParentAccountID,Month) from table group by Agent ,TCV ,ParentAccountID,Month
it ranks the values and the sorts the result.