Seemingly Simple Query in Pl Sql - sql

I have a table "defects" in the following format:
id status stat_date line div area
1 Open 09/21/09 F A cube
1 closed 01/01/10 F A cube
2 Open 10/23/09 B C Back
3 Open 11/08/09 S B Front
3 closed 12/12/09 S B Front
My problem is that I want to write a query that just extracts the "Open" defects. If I write a query to simply extract all open defects, then I get the wrong result because there are some defects,
that have 2 records associated with it. For example, with the query that I wrote I would get defect id#s 1 and 3 in my result even though they are closed. I hope I have explained my problem well. Thank you.

Use:
SELECT t.*
FROM DEFECTS t
JOIN (SELECT d.id,
MAX(d.stat_date) 'msd'
FROM DEFECTS d
GROUP BY d.id) x ON x.id = t.id
AND x.msd = t.stat_date
WHERE t.status != 'closed'
The join is getting the most recent date for each id value.
Join back to the original table on based on the id and date in order to get only the most recent rows.
Filter out those rows with the closed status to know the ones that are currently open

So you want to get the most recent row per id and of those, only select those that are open. This is a variation of the common greatest-n-per-group problem.
I would do it this way:
SELECT d1.*
FROM defects d1
LEFT OUTER JOIN defects d2
ON (d1.id = d2.id AND d1.stat_date < d2.stat_date)
WHERE d2.id IS NULL
AND d1.status = 'Open';

Select *
from defects d
where status = 'Open'
and not exists (
select 1 from defects d1
where d1.status = 'closed'
and d1.id = d.id
and d1.stat_date > d.stat_date
)

This should get what you want. I wouldn't have a record for open and closing a defect, rather just a single record to track a single defect. But that may not be something you can change easily.
SELECT id FROM defects
WHERE status = 'OPEN' AND id NOT IN
(SELECT id FROM defects WHERE status = 'closed')

This query handles multiple opens/closes/opens, and only does one pass through the data (i.e. no self-joins):
SELECT * FROM
(SELECT DISTINCT
id
,FIRST_VALUE(status)
OVER (PARTITION BY id
ORDER BY stat_date desc)
as last_status
,FIRST_VALUE(stat_date)
over (PARTITION BY id
ORDER BY stat_date desc)
AS last_stat_date
,line
,div
,area
FROM defects)
WHERE last_status = 'Open';

Related

List values with MaxDate

Im trying to create ie query to show itens with MAX DATE, but I donĀ“t know how !
Follow the script and result:
Select
results.severity As "Count_severity",
tasks.name As task,
results.host,
to_timestamp(results.date)::date
From
tasks Inner Join
results On results.task = tasks.id
Where
tasks.name Like '%CORP 0%' And
results.severity >= 7 And
results.qod > 70
I need to show only tasks with the last date of each one.
Can you help me ?
You seem to be using Postgres (as suggested by the use of casting operator ::). If so - and I follow you correctly - you can use distinct on:
select distinct on(t.name)
r.severity, t.name as task, r.host, to_timestamp(r.date::bigint)::date
from tasks t
inner join results r on r.task = t.id
where t.name like '%corp 0%' and r.severity >= 7 and r.qod > 70
order by t.name, to_timestamp(r.date::bigint)::date desc
This guarantees one row per task only; which row is picked is controlled by the order by clause, so the above gets the row with the greatest date (time portion left apart). If there are ties, it is undefined which row is returned. You might want to adapt the order by clause to your exact requirement, if it is different than what I understood.
On the other hand, if you want top ties, then use window functions:
select *
from (
select r.severity, t.name as task, r.host, to_timestamp(r.date::bigint)::date,
rank() over(partition by t.name order by to_timestamp(r.date::bigint)::date desc) rn
from tasks t
inner join results r on r.task = t.id
where t.name like '%corp 0%' and r.severity >= 7 and r.qod > 70
) t
where rn = 1

How show the last status of a mobile number and old data in the same row ? using SQL

I'm working in a telecom and part of work is to check the last status for a specific mobile number along with that last de-active status,it's easy to get the active number by using the condition ACTIVE int the statement ,but it's not easy to pick the last de-active status because each number might have more than one de-active status or only one status ACTIVE, I use the EXP_DATE as an indicator for the last de-active status,I want to show both new data and old data in one row,but I'm struggling with that ,below my table and my expected result :-
my expected result
query that I use on daily basis
select * from test where exp_date>sysdate; to get the active numbers , to get the de-active number select * from test where exp_date<sysdate;
You just need to do outer join with one subquery containing ACTIVE records and one with latest DE-ACTIVE record as following:
SELECT A.MSISDN,
A.NAME,
A.SUB_STATUS,
A.CREATED_DATE,
A.EXP_DATE,
D.MSISDN AS MSISDN_,
D.NAME AS OLD_NAME,
D.SUB_STATUS OLD_STATUS,
D.CREATED_DATE AS OLD_CREATED_DATE,
D.EXP_DATE AS OLD_EXP_DATE
FROM
(SELECT * FROM TEST
WHERE EXP_DATE > SYSDATE
AND SUB_STATUS = 'ACTIVE') A -- ACTIVE RECORD
-- USE CONDITION TO FETCH ACTIVE RECORD AS PER YOUR REQUIREMENT
FULL OUTER JOIN
(SELECT * FROM
(SELECT T.*,
ROW_NUMBER() OVER (PARTITION BY T.MSISDN ORDER BY EXP_DATE DESC NULLS LAST) AS RN
FROM TEST T
WHERE T.EXP_DATE < SYSDATE
AND T.SUB_STATUS='DE-ACTIVE')
-- USE CONDITION TO FETCH DEACTIVE RECORD AS PER YOUR REQUIREMENT
WHERE RN = 1
) D
ON (A.MSISDN = D.MSISDN)
Cheers!!
Here is an overview of how to do this -- one query to get a distinct list of all the phone numbers, left join to a list of the most recent active on that phone number,left join to a list of the most recent de-active on the phone number
How about conditional aggregation?
select msidn,
max(case when status = 'DE-ACTIVE' then create_date end) as deactive_date,
max(case when status = 'ACTIVE' then exp_date end) as active_date
from test
group by msisdn

Using a stored procedure in Teradata to build a summarial history table

I am using Terdata SQL Assistant connected to an enterprise DW. I have written the query below to show an inventory of outstanding items as of a specific point in time. The table referenced loads and stores new records as changes are made to their state by load date (and does not delete historical records). The output of my query is 1 row for the specified date. Can I create a stored procedure or recursive query of some sort to build a history of these summary rows (with 1 new row per day)? I have not used such functions in the past; links to pertinent previously answered questions or suggestions on how I could get on the right track in researching other possible solutions are totally fine if applicable; just trying to bridge this gap in my knowledge.
SELECT
'2017-10-02' as Dt
,COUNT(DISTINCT A.RECORD_NBR) as Pending_Records
,SUM(A.PAY_AMT) AS Total_Pending_Payments
FROM DB.RECORD_HISTORY A
INNER JOIN
(SELECT MAX(LOAD_DT) AS LOAD_DT
,RECORD_NBR
FROM DB.RECORD_HISTORY
WHERE LOAD_DT <= '2017-10-02'
GROUP BY RECORD_NBR
) B
ON A.RECORD_NBR = B.RECORD_NBR
AND A.LOAD_DT = B.LOAD_DT
WHERE
A.RECORD_ORDER =1 AND Final_DT Is Null
GROUP BY Dt
ORDER BY 1 desc
Here is my interpretation of your query:
For the most recent load_dt (up until 2017-10-02) for record_order #1,
return
1) the number of different pending records
2) the total amount of pending payments
Is this correct? If you're looking for this info, but one row for each "Load_Dt", you just need to remove that INNER JOIN:
SELECT
load_Dt,
COUNT(DISTINCT record_nbr) AS Pending_Records,
SUM(pay_amt) AS Total_Pending_Payments
FROM DB.record_history
WHERE record_order = 1
AND final_Dt IS NULL
GROUP BY load_Dt
ORDER BY 1 DESC
If you want to get the summary info per record_order, just add record_order as a grouping column:
SELECT
load_Dt,
record_order,
COUNT(DISTINCT record_nbr) AS Pending_Records,
SUM(pay_amt) AS Total_Pending_Payments
FROM DB.record_history
WHERE final_Dt IS NULL
GROUP BY load_Dt, record_order
ORDER BY 1,2 DESC
If you want to get one row per day (if there are calendar days with no corresponding "load_dt" days), then you can SELECT from the sys_calendar.calendar view and LEFT JOIN the query above on the "load_dt" field:
SELECT cal.calendar_date, src.Pending_Records, src.Total_Pending_Payments
FROM sys_calendar.calendar cal
LEFT JOIN (
SELECT
load_Dt,
COUNT(DISTINCT record_nbr) AS Pending_Records,
SUM(pay_amt) AS Total_Pending_Payments
FROM DB.record_history
WHERE record_order = 1
AND final_Dt IS NULL
GROUP BY load_Dt
) src ON cal.calendar_date = src.load_Dt
WHERE cal.calendar_date BETWEEN <start_date> AND <end_date>
ORDER BY 1 DESC
I don't have access to a TD system, so you may get syntax errors. Let me know if that works or you're looking for something else.

How to display row data in separate column in sql server 2012

I m having the data like this...
with cte as
(select *, rn = row_number() over (partition by empid order by trtime)
from [10.xx.xx.xx].[dbName].dbo.[tableName] where empid='00ec2137' and trdate='01/13/2014'
)
select i.empid,i.trdate, i.trtime InTime, o.trtime OutTime
from cte i
inner join cte o on i.empid = o.empid
and i.rn = o.rn - 1
where i.InOUt = 1
and o.InOUt = 2
order by i.empid,i.rn
InOut 1 - "In timing" and 2 - "out timing". I want the data to be ordered like In Timing and Out Timing in a separate column. I m using the above query in SQL 2012 to display the data like below
Getting Output
Expected Output
In the output it displayed the first two rows only. As there is no Out timing 2 in third row, third row is not displayed. Please suggest me how to correct the code.
simple I got this by modifying the inner join into left join and removed o.InOUt = 2 in where condtion
I think the problem is with the where clause. Try this where clause instead:
where (i.InOUt = 1)
and (o.InOUt = 2 or o.InOUt is null)
this will bring when the user has logged out or still logged in.

Grouping two dates as one in Oracle-SQL

I have a table 'task' with three relevant fields: date_created, date_updated, and is_closed.
I have a simple query that counts the number of tasks created:
SELECT task.date_created, count(task.is_closed)
FROM task
GROUP BY task.date_created
ORDER BY task.date_created
What I'd like is to also have the number of tasks closed per day. For our purposes, a task's final updated date is when is_closed='true'
So, the final table should look like
date opened closed
04/01/13 8 6
04/02/13 9 5
I think you need to do this as two subqueries. Here is one approach, using full outer join:
select coalesce(a.date_created, c.date_updated) as thedate,
coalesce(a.opened, 0) as opened,
coalesce(c.closed, 0) as closed
from (select date_created, count(*) as opened
from task
group by date_created
) a full outer join
(select date_updated, count(*) as closed
from task
where is_closed = 1 -- or whatever the value is
group by date_updated
) c
on a.date_created = c.date_updated
The full outer join guarantees that all dates are present, even when you have only closes or opens.
You can use a CASE statement with your COUNT:
SELECT task.date_created,
count(1) opened,
count(case when is_closed = 'true' then 1 end) closed
FROM task
GROUP BY task.date_created
ORDER BY task.date_created
SQL Fiddle Demo
Given your comments, here is an approach using a couple Common Table Expressions:
WITH OPENED AS (
SELECT date_created, count(1) opened
FROM task
GROUP BY date_created
) ,
CLOSED AS (
SELECT date_updated, count(1) closed
FROM task
WHERE is_closed = 'true'
GROUP BY date_updated
)
SELECT D.YourDateField, o.opened, c.closed
FROM YourDateTable D
LEFT JOIN Opened O ON D.YourDateField = O.Date_Created
LEFT JOIN CLosed C ON D.YourDateField = C.Date_Created
As Gordon points out, a FULL OUTER JOIN would also work. I just prefer using a dates table to seed from. Create the table once, and use it wherever you may need to.