Hard aggregation query - sql

I'm using Oracle SQL, and i need some help with a query. I have no idea how to do that.
I have the following table (table_a):
Mortgage_ID (int)
Doc_ID (int)
Status (varchar)
Each document can be sent many times for the same mortgage.
From the table above i've made the following table (table_b):
Rank (int)
Document_type (int)
Count (int)
This table is containing the global count of the top 40 popular documents from table_a (regardless the status). For example:
Rank | Doc_ID | count
--------------------------
1 | 212121 | 90
2 | 555111 | 82
3 | 4567654 | 76
. | . | .
. | . | .
. | . | .
40 | 54321 | 22
Now i need to create the following table: For each mortgage from table_a, I need the count of the documents that has been sent for each one of the top 40 documents with the status "OK".
For example:
Mortgage_id | Pop1 | Pop2 | Pop3 | ... | Pop40
-------------------------------------------------
123 | 50 | 21 | 30 | ... | 6
555 | 70 | 0 | 21 | ... | 40
654 | 100 | 96 | 58 | ... | 0
Pop1 doc (the most popular document) has been sent 50 times with the status "OK" for Mortgage_ID 123. Pop2 has been sent 21 times with status "OK" for Mortgage_id 123 and so on.
I hope the description is clear enough. Is anyone knows how to do that?

Basically, this is a join to combine the two tables and then a pivot. In this case, I would use conditional aggregation. So, I think this is what you are looking for:
select a.mortgage_id,
sum(case when b.rank = 1 then 1 else 0 end) as pop1,
sum(case when b.rank = 2 then 1 else 0 end) as pop2,
. . .
sum(case when b.rank = 40 then 1 else 0 end) as pop40
from table_b b join
table_a a
on b.doc_id = a.doc_id
group by a.mortgage_id;

Try this:
select *
from (select ta.Mortgage_ID, rank, cnt
from table_a ta, table_b tb
where ta.doc_id = tb.doc_id
)
pivot (
sum(cnt)
for rank in (1 pop1,2 pop2,3 pop3,4 pop4,5 pop5)
)
MORTGAGE_ID POP1 POP2 POP3 POP4 POP5
----------- ---------- ---------- ---------- ---------- ----------
1 20
2 40
5 10
4 5
3 30
SQLFiddle

Related

MS SQL Query to get all entries comming multiple times in table where some column value doesnt have entries

I just tried to formulate title as best as possible. So my case is as follow.
i have a table
venue_id | style_id | is_main
1 | 1 | 1
1 | 2 | 0
1 | 3 | 0
2 | 5 | 0
2 | 8 | 0
2 | 9 | 0
3 | 3 | 1
4 | 4 | 1
4 | 6 | 0
5 | 7 | 0
5 | 8 | 0
5 | 9 | 0
So i need to get only those venue ID, witch coming more then once and where is no is_main true entry.
So result should be contain venue_id's: 2 and 5
I would grateful for any suggestion how such query may looks like.
Thanks in Advance.
UPD: in my case with is_amin BIT value answer would be:
select venue_id
from table
group by venue_id
having cast(max(cast(is_main as INT)) AS BIT) = 0 and
count(*) >= 2;
You seem to want:
select venue_id
from t
group by venue_id
having max(is_main) = 0 and
count(*) >= 2;
You can use this one:
SELECT DISTINCT v.venue_id FROM venue v
LEFT OUTER JOIN (SELECT DISTINCT venue_id FROM venue WHERE is_main=1) m
ON v.venue_id = m.venue_id
WHERE m.venue_id IS NULL
If you have many thousands of rows, it would be better to create a secondary table or a materialized view to be used in place of the nested SELECT.

Count consecutive days

Please I want to count total consecutive days for an event record and order by this record grouping by actor id . for instance we have. For sqlite
event_id| created_at |actor_id
1 | 2018-07-01| 40 /* this is a consecutive days
1 | 2018-07-02| 40 */
1 | 2018-07-04| 40
1 | 2018-07-05| 40
1 | 2018-07-09| 40
2 | 2018-07-11| 40
2 | 2018-07-12| 40
1 | 2018-07-13| 41
should give me something like
actor_id|streak
40 | 3
41 | 0
You can group by actor_id and sum conditionally if there exists a consecutive day:
select
t.actor_id,
sum(case when exists (
select 1 from tablename
where
actor_id = t.actor_id and
julianday(created_at) - julianday(t.created_at) = 1
) then 1 else 0 end) streak
from tablename t
group by t.actor_id
See the demo.
Or with a self join:
select
t.actor_id,
sum(tt.created_at is not null) streak
from tablename t left join tablename tt
on tt.actor_id = t.actor_id and julianday(tt.created_at) - julianday(t.created_at) = 1
group by t.actor_id
See the demo.
Results:
| actor_id | streak |
| -------- | ------ |
| 40 | 3 |
| 41 | 0 |

missing expression error occured in case statement

I have source table Test contains id column, target table testm consist of id , col2 . Could please assist me to get rid of this error.
test (source)
id
---
10
10
20
20
20
30
30
40
Target
testm
id col2
-- ----
10 1
10 2
20 1
20 2
20 3
30 1
30 2
40 1
The query:
select id, (case id
when 10 then select count(id) from test where id =10
when 20 then select count(id) from test where id =20
when 30 then select count(id) from test where id =30
when 40 then select count(id) from test where id =40
else 0 END ) col2 from test
throws the error:
missing expression
From looking at the desired output I guess that you want to number each occurrence of an id by groups; if this is the case using the row_number analytical function should do what you want:
select id, row_number() over (partition by id order by id) as col2
from test
order by id;
See this sample SQL Fiddle
Given you sample source data this would be the output:
| ID | COL2 |
|----|------|
| 10 | 1 |
| 10 | 2 |
| 20 | 1 |
| 20 | 2 |
| 20 | 3 |
| 30 | 1 |
| 30 | 2 |
| 40 | 1 |

How to write a Sql query to find distinct values that have never met the following "Where Not(a=x and b=x)"

I have the following table called Attributes
* AttId * CustomerId * Class * Code *
| 1 | 1 | 1 | AA |
| 2 | 1 | 1 | AB |
| 3 | 1 | 1 | AC |
| 4 | 1 | 2 | AA |
| 5 | 1 | 2 | AB |
| 6 | 1 | 3 | AB |
| 7 | 2 | 1 | AA |
| 8 | 2 | 1 | AC |
| 9 | 2 | 2 | AA |
| 10 | 3 | 1 | AB |
| 11 | 3 | 3 | AB |
| 12 | 4 | 1 | AA |
| 13 | 4 | 2 | AA |
| 14 | 4 | 2 | AB |
| 15 | 4 | 3 | AB |
Where each Class, Code pairing represents a specific Attribute.
I'm trying to write a query that returns all customers that are NOT linked to the Attribute pairing Class = 1, Code = AB.
This would return Customer Id values 2 and 4.
I started to write Select Distinct A.CustomerId From Attributes A Where (A.Class = 1 and A.Code = 'AB') but stopped when I realised I was writing a SQL query and there is not an operator available to place before the parentheses to indicate the clause within must Not be met.
What am I missing? Or which operator should I be looking at?
Edit:
I'm trying to write a query that only returns those Customers (ie distinct Customer Id's) that have NO link to the Attribute pairing Class = 1, Code = AB.
This could only be Customer Id values 2 and 4 as the table does Not contain the rows:
* AttId * CustomerId * Class * Code *
| x | 2 | 1 | AB |
| x | 4 | 1 | AB |
Changed Title from:
How to write "Where Not(a=x and b=x)"in Sql Query
To:
How to write a Sql query to find distinct values that have never met the following "Where Not(a=x and b=x)"
As the previous title was a question in it's own right however the detail of the question added an extra dimension which led to confusion.
One way would be
SELECT DISTINCT CustomerId FROM Attributes a
WHERE NOT EXISTS (
SELECT * FROM Attributes forbidden
WHERE forbidden.CustomerId = a.CustomerId AND forbidden.Class = _forbiddenClassValue_ AND forbidden.Code = _forbiddenCodeValue_
)
or with join
SELECT DISTINCT a.CustomerId FROM Attributes a
LEFT JOIN (
SELECT CustomerId FROM Attributes
WHERE Class = _forbiddenClassValue_ AND Code = _forbiddenCodeValue_
) havingForbiddenPair ON a.CustomerId = havingForbiddenPair.CustomerId
WHERE havingForbiddenPair.CustomerId IS NULL
Yet another way is to use EXCEPT, as per ypercube's answer
SELECT CustomerId
FROM Attributes
EXCEPT
SELECT CustomerId
FROM Attributes
WHERE Class = 1
AND Code = AB ;
Since no one has posted the simple logical statement, here it is:
select . . .
where A.Class <> 1 OR A.Code <> 'AB'
The negative of (X and Y) is (not X or not Y).
I see, this is a grouping thing. For this, you use aggregation and having:
select customerId
from Attributes a
group by CustomerId
having sum(case when A.Class = 1 and A.Code = 'AB' then 1 else 0 end) = 0
I always prefer to solve "is it in a set" type questions using this technique.
Select Distinct A.CustomerId From Attributes A Where not (A.Class = 1 and A.Code = 'AB')
Try this:
SELECT DISTINCT A.CustomerId From Attributes A Where
0 = CASE
WHEN A.Class = 1 and A.Code = 'AB' THEN 1
ELSE 0
END
Edit: of course this still gives you cust 1 (doh!), you should probably use pjotrs NOT EXISTS query ideally, serves me right for not looking at the data closely enough :)

Efficent way to generate a summery table in SQL. Please see explanation

I am very new to sql.I need some help in generating summery information
MemberTable
MonthID | UserID | TeamID
-----------------------------
1 | 1 | 1
1 | 2 | 1
1 | 3 | 1
1 | 4 | 1
1 | 5 | 2
1 | 6 | 2
1 | 7 | 2
AND
ReportTable
ID* | MonthID | UserID | IsSend
-----------------------------------
1 | 1 | 2 | False
2 | 1 | 3 | True
3 | 1 | 5 | True
I want to generate a summery like the following
TeamID | Total Count | Send Count | Not Send Count
-----------------------------------------------------------
1 | 4 | 1 | 3
2 | 3 | 1 | 2
Total Count : No of users in a team
Send Count : Total User in a team with IsSend = True
Not Send Count : Total Count - Send Count
What would be the efficent way?
Give this a try:
select mt.teamId, count(*) totalCount,
count(case when rt.isSend = 'True' then 1 end) sendCount,
count(case when rt.isSend != 'True' then 1 end) notSendCount
from memberTable mt
join reportTable rt on mt.userId = rt.userId
group by mt.teamId
Note that your expected result does not reflect your data. The result based on your data should be:
+--------+------------+-----------+--------------+
| TEAMID | TOTALCOUNT | SENDCOUNT | NOTSENDCOUNT |
+--------+------------+-----------+--------------+
| 1 | 2 | 1 | 1 |
| 2 | 1 | 1 | 0 |
+--------+------------+-----------+--------------+
select MT.TeamID,
count(distinct MT.UserID) as "Total Count",
count(distinct case when RT.IsSend = 1 then MT.UserID end) as "Send Count",
count(distinct MT.UserID) - count(distinct case when RT.IsSend = 1 then MT.UserID end) as "Not Send Count"
from MemberTable as MT
left outer join ReportTable as RT
on MT.MonthID = RT.MonthID and
MT.UserID = RT.UserID
group by MT.TeamID
Result:
TeamID Total Count Send Count Not Send Count
----------- ----------- ----------- --------------
1 4 1 3
2 3 1 2
Try here: https://data.stackexchange.com/stackoverflow/query/66347
Without havign the tables to try this on, I can't check that this will work, but this shoul get you most of the way:
SELECT TeamID, count(userID) as "Total count", Sum(IsSend) as "Send Count" FROM MemberTable JOIN ReportTable ON UserID GROUP BY TeamID;