oracle query with groupby clause - sql

i have a upload table as follows:
bulk_upload_hist
id file_name doc_blob upload_date upload_by
10 abc.pdf 12-APR-21 123
11 xyz.pdf 12-APR-21 123
inventory history stores the records from the file as follows:
inventory_doc_hist
id upload_id upload_status create_date create_by inv_doc_type
1 10 1 12-APR-21 123 20
2 10 1 12-APR-21 123 20
3 10 0 12-APR-21 123 10
4 10 1 12-APR-21 123 10
4 11 1 12-APR-21 123 20
5 11 0 12-APR-21 123 10
I want my output per bulk upload as follows:
id file_name successful/10 Successful/20 UnSuccessful upload_date upload_by
10 abc.pdf 2 1 1 12-APR-21 123
11 xyz.pdf 1 1 0 12-APR-21 123
what is the best way to do this?

I think you want a join and conditional aggregation:
select buh.id, buh.file_name,
sum(case when ih.inv_doc_type = 10 then 1 else 0 end) as num_successful_10,
sum(case when ih.inv_doc_type = 20 then 1 else 0 end) as num_successful_20,
sum(case when ih.inv_doc_type not in (10, 20) then 1 else 0 end) as num_successful_20,
ih.upload_date, ih.upload_by
from bulk_upload_hist buh join
inventory_history ih
on ih.upload_id = ih.id
group by buh.id, buh.file_name, ih.upload_date, ih.upload_by;

Related

Pairwise swapping of rows in sql

I have a device settings table with threshold types and values.
Let's say ThresholdType = 0 is Min and ThresholdType = 1 is Max
The initial table looks like this:
DeviceID ThresholdType ThresholdValue
1 0 5
1 1 10
2 0 15
2 1 20
3 0 21
3 1 34
4 0 1
4 1 8
Then I had to change ThresholdType meaning - 0 became Max threshold and 1 became Min one.
I want the table look like that:
DeviceID ThresholdType ThresholdValue
1 0 10
1 1 5
2 0 20
2 1 15
3 0 34
3 1 21
4 0 8
4 1 1
Is it possible to change update it with a single SQL without loops?
Update ThresholdType instead:
update tablename set ThresholdType = 1 - ThresholdType
In case other ThresholdType values might show up later, you can add WHERE ThresholdType IN (1, 2), to be a bit safer.
Just swap the ThresholdType:
UPDATE t SET ThresholdType = CASE ThresholdType
WHEN 1 THEN 0
WHEN 0 THEN 1
ELSE ThresholdType
END
Execute the query exactly once.
You can do:
update t
set ThresholdValue = (
select x.ThresholdValue
from t x
where x.DeviceID = t.DeviceID and x.ThresholdType <> t.ThresholdType
)
Result:
DeviceID ThresholdType ThresholdValue
--------- -------------- --------------
1 0 10
1 1 5
2 0 20
2 1 15
See running example at db<>fiddle.

SQL update column depending on other column

sequenceNo IsPoint PointNumber PointSequenceNumber IsCancel
1 1 3168 1 1
2 0 NULL 2 1
3 1 3169 2 1
4 1 2806 3 1
5 1 33322 4 1
6 1 2807 5 1
7 1 2044 6 1
8 1 2046 7 1
9 0 NULL 8 1
10 1 27524 8 1
11 1 670 9 0
12 1 671 10 0
13 1 672 11 0
14 0 NULL 12 1
15 1 1074 12 1
16 1 10844 13 0
17 1 1421 14 0
I need to insert PointNumber to other table depending on value of IsCancel Column if IsCancel is set to 1 I need to insert first iscancel and last IsCancel but sequentially
e.g. STARTPOINT => above sequenceNo 1 for point number 3168 and Iscancel set to 1 I need that point to store in other table StartPoint
ENDPOINT => above sequenceNo 15 for point number 1074 and Iscancel set to 1 I need that point to store in other table as Endpoint
TotalCANCELED => Column hold COUNT IsCancel but only if PointNumber is not NULL
I need to update these columns in other table with following numbers depending on above table
STARTPOINT ENDPOINT TOTALCANCEL
3168 1074 9
Thanks community
I find this logic a bit hard to follow. If I understand this correctly, you can use aggregation -- although you need a little twist to get the end point number:
select max(case when PointSequenceNumber = 1 then pointnumber end) as startpoint,
max(case when PointSequenceNumber = psn then pointnumber end) as endpoint,
sum(case when isCancel = 1 and PointNumber is not null end) as totalCancel
from (select t.*,
max(case when isCancel = 1 and PointNumber is not null then PointSequenceNumber end) over () as max_psn
from t
) t;

How to find count from two joined tables

We have to find count for each risk category for impact level as shown in last result part
Risk Table
RiskID RiskName
----------------------
1 Risk1
2 Risk2
3 Risk3
4 Risk4
5 Risk5
6 Risk6
7 Risk7
8 Risk8
9 Risk9
10 Risk10
11 Risk11
Category Table
Cat_ID Cat_Name
--------------------------
1 Design
2 Operation
3 Technical
Risk_Category table
Risk_ID Category_ID
------------------------
1 1
1 2
2 1
3 1
3 3
4 1
5 2
6 1
7 3
8 1
9 3
10 3
Risk_Impact_Assessment table
Risk_ID Impact_Level Impact_Score
---------------------------------------------
1 High 20
2 Medium 15
3 High 20
4 Low 10
5 High 20
6 High 20
7 High 20
8 Low 10
9 Medium 15
10 Low 15
11 Medium 15
Result should be like this
Cat_Name Impact_Level_High Impact_Level_Medium Impact_Level_Low
-------------------------------------------------------------------------------------
Design 1 1 2
Operation 2
Technical 2 2 1
You probably want to use the group by clause, along with case, eg.:
select
Cat_Name,
sum(case when Impact_Level = 'High' then 1 else 0 end) as [Impact_Level_High],
sum(case when Impact_Level = 'Medium' then 1 else 0 end) as [Impact_Level_Medium],
sum(case when Impact_Level = 'Low' then 1 else 0 end) as [Impact_Level_Low]
from [Risk_Impact_Assessment]
...
group by Cat_Name;
(I left out all the joins, I assume you can write these no problem)
You can use this trick to accomplish a lot of cool things, including parametric sorting and (just like here) complicated aggregate functions with little work.

How to write SQL query for aggregate total value for a particular condition

vote table
PK_ID ob_type ob_id VOTE user_id VOTED_ON
1 100 1 1 NISHANT 02/08/10
2 100 1 1 DEVI 02/08/10
3 100 1 1 VIRENDRA 02/08/10
4 100 1 0 XYZ 02/08/10
5 200 1 1 XCXCX 02/08/10
6 200 1 1 CXC 03/02/11
7 200 1 0 XCCX 03/02/11
8 200 1 0 XCCX 03/02/11
9 300 1 1 XCCXXC 03/02/11
10 300 1 1 XCCX 03/02/11
11 300 1 1 SD 02/08/10
12 300 1 0 XCCXXCC 02/08/10
13 400 4 0 CXXCXC 02/08/10
14 400 4 0 XCCX 03/02/11
15 400 4 0 CXC 03/02/11
16 400 1 1 CXC 03/02/11
here I want to calculate total no of vote on a particular object type and id.
SELECT
COUNT (ALL [PK_ID]) AS [TOTAL_VOTE],
COALESCE (
( SELECT
[IS_THUMBS_UP]
FROM
[votetable]
WHERE
[ob_type] = 400 AND
[ob_id] = 4 AND
[FK_VOTED_BY] = Nishant ),-1) AS [MY_VOTING]
FROM
[votetable]
WHERE
[ob_type] = 400 AND
[ob_id] = 4
Here it give result
total vote = 4
and my vote = 0
but here total voting is -2 (bcz 3 false(0) and 1 true)
So how do I get the correct value.
I suggest (SQLServer syntax) :
select sum(case [VOTE] when 1 then 1 else -1 end) as total_votes,
sum((case [VOTE] when 1 then 1 else -1 end) *
(case when [user_id] = #username then 1 else 0) as user_votes
from [votetable]
where [ob_type] = #obtype and [ob_id] = #obid
#username, #obtype and #obid are variables with the desired user's name, object type and object ID.

SQL for MS Access: Another question about COUNT, JOIN, 0s and Dates

I asked a question regarding joins yesterday. However although that answer my initial question, i'm having more problems.
I have a telephony table
ID | Date | Grade
1 07/19/2010 Grade 1
2 07/19/2010 Grade 1
3 07/20/2010 Grade 1
4 07/20/2010 Grade 2
5 07/21/2010 Grade 3
I also have a Grade table
ID | Name
1 Grade 1
2 Grade 2
3 Grade 3
4 Grade 4
5 Grade 5
6 Grade 6
7 Grade 7
8 Grade 8
9 Grade 9
10 Grade 10
11 Grade 11
12 Grade 12
I use the following query to get the COUNT of every grade in the telephony table, it works great.
SELECT grade.ID, Count(telephony.Grade) AS Total
FROM grade LEFT JOIN telephony ON grade.ID=telephony.Grade
GROUP BY grade.ID
ORDER BY 1;
This returns
ID | Total
1 3
2 1
3 1
4 0
5 0
6 0
7 0
8 0
9 0
10 0
11 0
12 0
However, what i'm trying to do is the following:
Group by date and only return results between two dates
SELECT telephony.Date, grade.ID, Count(telephony.Grade) AS Total
FROM grade LEFT JOIN telephony ON grade.ID=telephony.Grade
WHERE telephony.Date BETWEEN #07/19/2010# AND #07/23/2010#
GROUP BY telephony.Date, grade.ID
ORDER BY 1;
I'm getting the following
Date | ID | Total
07/19/2010 1 2
07/20/2010 1 1
07/20/2010 2 1
07/21/2010 3 1
It's not returning all the grades with 0 entries between the two dates, only the entries that exist for those dates. What i'm looking for is something like this:
Date | ID | Total
07/19/2010 1 2
07/19/2010 2 0
07/19/2010 3 0
07/19/2010 4 0
07/19/2010 5 0
07/19/2010 6 0
07/19/2010 7 0
07/19/2010 8 0
07/19/2010 9 0
07/19/2010 10 0
07/19/2010 11 0
07/19/2010 12 0
07/20/2010 1 1
07/20/2010 2 1
07/20/2010 3 0
07/20/2010 4 0
07/20/2010 5 0
07/20/2010 6 0
07/20/2010 7 0
07/20/2010 8 0
07/20/2010 9 0
07/20/2010 10 0
07/20/2010 11 0
07/20/2010 12 0
07/21/2010 1 2
07/21/2010 2 0
07/21/2010 3 1
07/21/2010 4 0
07/21/2010 5 0
07/21/2010 6 0
07/21/2010 7 0
07/21/2010 8 0
07/21/2010 9 0
07/21/2010 10 0
07/21/2010 11 0
07/21/2010 12 0
07/22/2010 1 2
07/22/2010 2 0
07/22/2010 3 0
07/22/2010 4 0
07/22/2010 5 0
07/22/2010 6 0
07/22/2010 7 0
07/22/2010 8 0
07/22/2010 9 0
07/22/2010 10 0
07/22/2010 11 0
07/22/2010 12 0
07/23/2010 1 2
07/23/2010 2 0
07/23/2010 3 0
07/23/2010 4 0
07/23/2010 5 0
07/23/2010 6 0
07/23/2010 7 0
07/23/2010 8 0
07/23/2010 9 0
07/23/2010 10 0
07/23/2010 11 0
07/23/2010 12 0
I hope someone can help. I'm using Microsoft Access 2003.
Cheers
Create a separate query on telephony which uses your BETWEEN #07/19/2010# AND #07/23/2010# constraint.
qryTelephonyDateRange:
SELECT *
FROM telephony
WHERE [Date] BETWEEN #07/19/2010# AND #07/23/2010#;
Then, in your original query, use:
LEFT JOIN qryTelephonyDateRange ON grade.ID=qryTelephonyDateRange.Grade
instead of
LEFT JOIN telephony ON grade.ID=telephony.Grade
You could use a subquery instead of a separate named query for qryTelephonyDateRange.
Note Date is a reserved word, so I bracketed the name to avoid ambiguity ... Access' database engine will understand it is supposed to be looking for a field named Date instead of the VBA Date() function. However, if it were my project, I would rename the field to avoid ambiguity ... name it something like tDate.
Update: You asked to see a subquery approach. Try this:
SELECT g.ID, t.[Date], Count(t.Grade) AS Total
FROM
grade AS g
LEFT JOIN (
SELECT Grade, [Date]
FROM telephony
WHERE [Date] BETWEEN #07/19/2010# AND #07/23/2010#
) AS t
ON g.ID=t.Grade
GROUP BY g.ID, t.[Date]
ORDER BY 1, 2;
Try this:
SELECT grade.ID, Count(telephony.Grade) AS Total
FROM grade LEFT JOIN telephony ON grade.ID=telephony.Grade
GROUP BY grade.ID
HAVING COUNT(telephony.Grade) > 0
ORDER BY grade.ID;
That's completely different.
You want a range of individual dates joined with your first table, and the between clause isn't going to do that for you.
I think you'll need a table with all the dates you want, say from 1/1/2010 to 12/31/2010, or whatever range you need to support. One column, 365 or however many rows with one date value each.
then join that table with the ones with the dates and grades, and limit by your date range,
then do the aggregation to count.
Take it one step at a time and it will be easier to figure out.
The way I got it to work was to:
Create a table named Dates with a single primary key date/time field named MyDate (I'm with HansUp on not using reserved words like "Date" for field names).
Fill the table with the date values I wanted (7/19/2010 to 7/23/2010, as in your example).
Write a query with the following SQL statement
SELECT x.MyDate AS [Date], x.ID, Count(t.ID) AS Total
FROM (SELECT Dates.MyDate, Grade.ID FROM Dates, Grade) AS x
LEFT JOIN Telephony AS t ON (x.MyDate = t.Date) AND (x.ID = t.Grade)
GROUP BY x.MyDate, x.ID;
That should get the results you asked for.
The subquery statement in the SQL creates a cross-join to get you every combination of date in the Dates table and grade in the Grade table.
(SELECT Dates.MyDate, Grade.ID FROM Dates, Grade) AS x
Once you have that, then it's just an outer join to the Telephony table to do the rest.