Oracle DB - count non-consecutive actions - sql

Hi I have the scenario where the SQL in oracle db, should return the count of the shown variables value as below.
Action field will have Insert and Delete as a value for different time stamp but when I report the count it should be like for the given input.
The logic is all the Insert or all the Delete should be counted as 1 till it hits delete or Insert respectively.
Like a call center a customer can add any number of compliant (Insert) but that should be counted as 1 for that user id, if the already registered complaint is resolved (Delete) then next sequence of complaint (Insert) should be counted as one.
Any help is highly appreciated and Many thanks in advance!
Deva

Try this:
select keyfield, action, count(*) from (
select lead(action, 1, 'other') over (partition by keyfield order by timestamp) as naction, action from t1)
where action != naction group by action, keyfield;
Idea is to use analytic function to select only those records which are followed by other (ignore chains of same actions) action. Then group by action and id to do count.

Related

SQL: Insert a new unique row based off previous row

I'm working with Deltek Vision ERP software trying to create a custom solution to provide a list of deliverables for projects. I've created a custom grid in the Project area of Vision that has a table in SQL structured like this:
SELECT TOP (1000) [WBS1]
,[WBS2]
,[WBS3]
,[Seq]
,[CreateUser]
,[CreateDate]
,[ModUser]
,[ModDate]
,[CustDeliverables]
,[CustDueDate]
,[CustCompletionDate]
FROM [Vision_Prod].[dbo].[Projects_Deliverables]
The table has three user entered fields, which are the last three columns listed above.
What I'm trying to accomplish is have deliverables set at WBS2 level also roll up to WBS1, so basically what needs to happen is that any time a record is created with a value in WBS2 the record is duplicated but the duplicate has no value in WBS2.
I've setup a workflow in Vision so that when someone enters a deliverable into the grid on a phase it kicks off a stored procedure to accomplish this. The problem is the Seq field. This is a unique identifier the system is assigning when a record is created. When my stored procedure fires I'm getting an error that the sequence has to be included in the record.
This is the stored procedure I'm using:
INSERT INTO [Vision_Prod].[dbo].[Projects_Deliverables] (WBS1, WBS2, WBS3, Seq, CreateUser, ModUser, ModDate, CreateDate, CustDueDate, CustCompletionDate)
SELECT WBS1, '', '', (NEXT VALUE FOR Seq), CreateUser, ModUser, ModDate, CreateDate, CustDueDate, CustCompletionDate
FROM Projects_Deliverables
WHERE Projects_Deliverables.WBS2 IS NOT Null and Projects_Deliverables.WBS1 = #WBS1
If anyone can help me figure out how to get the system to assign a new sequence when a record is created in this way that would be most appreciated.
You could create a dummy table using VALUES to create the an extra row and then fiter out when not needed. Something (but not tested) like this:
SELECT TOP (1000)
[WBS1]
,CASE c WHEN 2 THEN NULL ELSE [WBS2] END AS [WBS2]
,[WBS3]
,[Seq]
,[CreateUser]
,[CreateDate]
,[ModUser]
,[ModDate]
,[CustDeliverables]
,[CustDueDate]
,[CustCompletionDate]
FROM [Vision_Prod].[dbo].[Projects_Deliverables]
CROSS JOIN (VALUES (1), (2)) t(c)
WHERE (WBS2 IS NULL AND c < 2)
OR WBS2 IS NOT NULL
This was the solution:
REPLACE(CAST(NEWID() AS VARCHAR(36)), '-', '')

Get cell value and use it in an INSERT statement (MS ACCESS)

I'd like to insert a record using values from different tables
just to illustrate:
INSERT INTO tbl1
VALUES([value1], [value2], NOW())
value1 is a number that comes from a cell in a random row in a different table
value2 is another number that comes from a cell in a random row in another table
Here's how I'm trying to do it:
INSERT INTO transactions(itemid, userid, tdate)
VALUES((SELECT TOP 1 ID FROM items ORDER BY RND(ID)), (SELECT TOP 1 ID FROM users ORDER BY RND(ID)), (NOW()))
But this throws an error:
Query input must contain at least one table or query
Any help would be greatly appreciated.
Thanks!
You could rewrite your statement to use a SELECT instead of VALUES:
INSERT INTO transactions(itemid, userid, tdate)
SELECT TOP 1 items.ID, users.ID, NOW()
FROM items, users
ORDER BY Rnd(-(1000*items.ID*users.ID)*Time()),
items.ID, users.ID
Edit: I added the ORDER BY clause which will lead to more random sort orders. The negative value will ensure a sort of randomize. See also this question.
Edit2: extended ORDER BY clause to ensure TOP 1 will not have to deal with ties.

if-then-else construction in complex stored procedure

I am relatively new to sql queries and I was wondering how to create a complex stored procedure. My database runs on SQL server.
I have a table customer (id, name) and a table customer_events (id, customer_id, timestamp, action_type). I want add a calculated field customer_status to table customer which is
0: (if there is no event for this customer in customer_events) or (the most recent event is > 5 minutes ago)
1: if the most recent event is < 5 minutes ago and action_type=0
2: if the most recent event is < 5 minutes ago and action_type=1
Can I use if-then-else constructions or should I solve this challenge differently?
As you mentioned in comments, you actually want to add a field to a select query, and in a general sense what you want is a CASE statement. They work like this:
SELECT field1,
field2,
CASE
WHEN some_condition THEN some_result
WHEN another_condition THEN another_result
END AS field_alias
FROM table
Applied to your specific scenario, well it's not totally straightforward. You're certainly going to need to left join your status table, you also want to aggregate to find the most recent event, along with that event's action type. Once you have that information, the case statement is straightforward.
Always hard to write sql without access to your data, but something like:
SELECT c.id,
c.name,
CASE
WHEN e.id IS NULL OR DATEDIFF(minute,e.timestamp,getDate())>=5 THEN 0
WHEN DATEDIFF(minute,e.timestamp,getDate())<5 AND s.action_type=1 THEN 1
WHEN DATEDIFF(minute,e.timestamp,getDate())<5 AND s.action_type=0 THEN 2
END as customer_status
FROM clients c
LEFT JOIN (
SELECT id, client_id, action_type,
rank() OVER(partition by client_id order by timestamp desc) AS r
FROM customer_events
) e
ON c.id=e.client_id AND e.r=1
The core of this is the subquery in the middle, it's using a rank funtion to give a number to each status by client_id ordered by the timestamp descending. Therefore every record with a rank of 1 will be the most recent (for that client). Thereafter, you simply join it on to the client table, and use it to determine the right value for customer_status
Presuming you get the event info into "Most_Recent_Event_Mins_Ago". If none it will be NULL.
SELECT Id, Name,
CASE
WHEN Most_Recent_Event_Mins_Ago IS NULL THEN 0
WHEN Most_Recent_Event_Mins_Ago <5 AND Action_type = 0 THEN 1
WHEN Most_Recent_Event_Mins_Ago <5 AND Action_type = 1 THEN 0
..other scenarions
ELSE yourDefaultValueForStatus
END as Status
FROM customer
WHERE
...
...

Write Oracle SQL query to fetch from Tasks table top Approval Statuses that appear after some first null value

Write Oracle SQL query to fetch from Tasks table top Approval Statuses that appear after some first null value in the Approval_Status Column and then Approval Status sequence and then some null values
Facts
I only need the top Approval Statuses sequence
Serial Number for each task ID Sequence starts from 1 and then comes in Sequence like 1.2.3... and so on
There are thousands of tasks in the table like from T1 .... Tn
See the Query Result below i need to write a query that returns data in that format
I have heard analytic function i.e. "Partition By clause" for this can be used but i don't know how to use that
Tasks
Query Result
I really appreciate experts help in this regard
Thanks
You can do this with analytic functions, but there is a trick. The idea is to look only at rows where approval_status is not null. You want the first group of sequential serial numbers in this group.
The group is identified by the difference between a sequence that enumerates all the rows and the existing serial number. To get the first, use dense_rank(). Finally, choose the first by looking for the ones with a rank equal to 1:
select t.*
from (select t.*, dense_rank(diff) over (partition by taskid) as grpnum
from (select t.*,
(row_number() over (partition by taskid order by serial_number) -
serial_number
) as diff
from tasks
where approval_status is not null
) t
) t
where grpnum = 1;

SQL count, use only last record

can someone help me about counting rows in sql. I have a table, archive, in which I have bank account and status of that account. One account can have and usually have more records, in my count I have to use last record, not records before. Example:
account status
5552222 A
5552222 B
5552222 A
**5552222 B**
4445896 A
4445896 B
**4445896 A**
I have to use this who are bold. Based on this there is one B(blocked) and one A(active) account. I have column datetime, which can tell me what is last record. I just need query to count that
Assuming you want to count based on the most current row for an account:
SELECT tab.status,
COUNT(*)
FROM tab JOIN
(
SELECT account, MAX(datetime) AS maxdate
FROM tab
GROUP BY account
) AS dt
ON tab.account = dt.account
AND tab.datetime = dt.maxtime
GROUP BY tab.Status
SELECT COUNT(*)
FROM yourTable
WHERE Status='B'
or
WHERE AccountName LIKE '%B'
Edit: After OP modified the question to include the table data.
So, the problem is that the same account number can occur multiple times, and you want to count on the basis of last status of the account.
If the account is currently blocked, you would like to count it, irrespective of the number of times it gets blocked earlier.
Assumption: You have a date type column in your table which shows the date when the record's (with new status value) was inserted (or it may be an identity field which keeps track of the order of records created in the table)
The query will be:
SELECT COUNT (*)
FROM
(
SELECT DISTINCT
acNumber,
( SELECT Max(identityField_or_dateField)
FROM tableName t
WHERE t.acNumber = t2.acNumber AND Status='B')
FROM tableName t2
WHERE
( SELECT Max(identityField_or_dateField)
FROM tableName t
WHERE t.acNumber = t2.acNumber AND Status='B') IS NOT NULL
) tblAlias
Glad to help! Please remember to accept the answer if you found it helpful.