delete records if 2 counts match - sql

I have the following table with data
+---------+--------+--------+
| Trans | Status | Step |
+---------+--------+--------+
| ABCDE | Comple | 1
+---------+--------+--------+
| ABCDE | Error | 2
+---------+--------+--------+
| FGHIJ | Comple | 1
+---------+--------+--------+
| FGHIJ | Comple | 2
+---------+--------+--------+
| KLMNO | Comple | 1
+---------+--------+--------+
| KLMNO | Comple | 2
+---------+--------+--------+
I only want to delete the records where the count of trans and status = 'comple' is the same as count of trans
I could probably do a cursor and loop but that is something I don't want to do.
Thinking along the lines of a having count but probably miles off.
Thanks
I just want to delete FGHIJ and KLMNO as I know all the steps completed.
I want to keep abcde as not all steps completed

Is this what you want?
--delete
select *
from yourtable t
where not exists
(
select 1
from yourtable t2
where t1.Trans=t2.Trans
and status<>'Comple'
)
Use it as it is first to make sure this is what you want to remove, then comment out the SELECT and uncomment the delete

DELETE FROM tbl
WHERE Trans IN ( SELECT trans
FROM tbl
GROUP BY Trans
HAVING COUNT(*) = SUM(CASE WHEN status = 'Comple' THEN 1 ELSE 0 END) )
It deletes rows (by filtering on Trans), where the number of rows in a group of trans is equal to the number of rows in this group with the Comple status
http://sqlfiddle.com/#!18/1768b/10

Try this
DECLARE #Data AS TABLE (Trans Varchar(10) , Status Varchar(10) , Step INT )
INSERT INTO #Data
SELECT 'ABCDE', 'Comple' , 1 UNION ALL
SELECT 'ABCDE', 'Error' , 2 UNION ALL
SELECT 'FGHIJ', 'Comple' , 1 UNION ALL
SELECT 'FGHIJ', 'Comple' , 2 UNION ALL
SELECT 'KLMNO', 'Comple' , 1 UNION ALL
SELECT 'KLMNO', 'Comple' , 2
SELECT * FROM #Data
;WITH CTe
AS
(
SELECT * , ROW_NUMBER()OVER(PArtition by Trans , [Status] ORDER BY Trans) AS TwocountsMatch
FROM #Data
)
DELETE FROM CTe WHERE TwocountsMatch=2 AND [Status]='Comple'
SELECT * FROM #Data
Demo Result :http://rextester.com/TYK92230

If I understand you correctly, you want to delete all of a [Trans] rows, if all of those rows [status] are 'Comple'?
DECLARE #Data AS TABLE (Trans Varchar(10) , Status Varchar(10) , Step INT )
INSERT INTO #Data
SELECT 'ABCDE', 'Comple' , 1 UNION ALL
SELECT 'ABCDE', 'Error' , 2 UNION ALL
SELECT 'FGHIJ', 'Comple' , 1 UNION ALL
SELECT 'FGHIJ', 'Comple' , 2 UNION ALL
SELECT 'KLMNO', 'Comple' , 1 UNION ALL
SELECT 'KLMNO', 'Comple' , 2
SELECT * FROM #Data;
WITH
summarised
AS
(
SELECT
* ,
COUNT(*) OVER (PARTITION BY [trans] ) AS count_trans,
COUNT(*) OVER (PARTITION BY [trans], [status]) AS count_trans_status
FROM
#Data
)
DELETE
summarised
WHERE
[status] = 'Comple'
AND count_trans = count_trans_status
;
SELECT * FROM #Data

Related

How to exclude certain rows from sql select

How do I exclude certain rows?
For example, I have the following table:
+------+------+------+
| Col1 | Col2 | Col3 |
+------+------+------+
| 1 | 1 | R |
| 1 | 2 | D |
| 2 | 3 | R |
| 2 | 4 | R |
| 3 | 5 | R |
| 4 | 6 | D |
+------+------+------+
I need to select only:
| 2 | 3 | R |
| 2 | 4 | R |
| 3 | 5 | R |
My select that does not work properly:
with t (c1,c2,c3) as(
select 1 , 1 , 'R' from dual union all
select 1 , 2 , 'D' from dual union all
select 2 , 3 , 'R' from dual union all
select 2 , 4 , 'R' from dual union all
select 3 , 5 , 'R' from dual union all
select 4 , 6 , 'D' from dual),
tt as (select t.*,count(*) over (partition by c1) cc from t ) select * from tt where cc=1 and c3='R';
Thanks in advance!
select * from table where col2 = 'R'
or if you want to exclude rows with D value just
select * from table where col2 != 'D'
It depends on your requirements but you can do in this way:
SELECT * FROM `table` WHERE col1 = 2 AND col3 = "R"
if you want to exclude just do it like WHERE col1 != 1
You ca also use IN clause also e.g.
SELECT column_name(s)
FROM table_name
WHERE column_name IN (value1, value2, ...);
This syntax is for MySql, but you can modify it as per your requirement or database you are using.
this will work :
select * from (select * from table_name) where rownum<=4
minus
select * from ( select * from table_name) where rownum<=2
My guess is that you want all rows for a col1 where no row for a col1 = D and at least 1 row for a col1 = R. # where [not] exists may do
DROP TABLE T;
CREATE TABLE T
(Col1 NUMBER, Col2 NUMBER, Col3 VARCHAR(1));
INSERT INTO T VALUES ( 1 , 1 , 'R');
INSERT INTO T VALUES ( 1 , 2 , 'D');
INSERT INTO T VALUES ( 2 , 3 , 'R');
INSERT INTO T VALUES ( 2 , 4 , 'R');
INSERT INTO T VALUES ( 3 , 5 , 'R');
INSERT INTO T VALUES ( 3 , 6 , 'D');
INSERT INTO T VALUES ( 4 , 5 , 'X');
INSERT INTO T VALUES ( 4 , 6 , 'Y');
INSERT INTO T VALUES ( 5 , 6 , 'X');
INSERT INTO T VALUES ( 5 , 5 , 'R');
INSERT INTO T VALUES ( 5 , 6 , 'Y');
SELECT *
FROM T
WHERE NOT EXISTS(SELECT 1 FROM T T1 WHERE T1.COL1 = T.COL1 AND COL3 = 'D') AND
EXISTS(SELECT 1 FROM T T1 WHERE T1.COL1 = T.COL1 AND COL3 = 'R');
Result
COL1 COL2 COL3
---------- ---------- ----
5 6 X
5 5 R
5 6 Y
2 3 R
2 4 R
use row_number() window function
with t (c1,c2,c3) as(
select 1 , 1 , 'R' from dual union all
select 1 , 2 , 'D' from dual union all
select 2 , 3 , 'R' from dual union all
select 2 , 4 , 'R' from dual union all
select 3 , 5 , 'R' from dual union all
select 4 , 6 , 'D' from dual
),
t1 as
(
select c1,c2,c3,row_number() over(order by c2) rn from t
) select * from t1 where t1.rn>=3 and t1.rn<=5
demo link
C1 C2 C3
2 3 R
2 4 R
3 5 R
You can try using correlated subquery
select * from tablename a
from
where exists (select 1 tablename b where a.col1=b.col1 having count(*)>1)
Based on what you have provided I can only surmise that the only requirement is for COL1 to be equal to 2 or 3 in that case all you have to do is (assuming that you actually have table);
SELECT * FROM <table_name>
WHERE col1 IN (2,3);
This will give you the desired output for the particular example provided in the question. If there is a selection requirement that goes beyond retrieving data where column 1 is either 2 or 3 than a more specific or precise answer can be provided.

How do I find specific values in a table in Oracle?

Suppose my table is TEST_123 Which has the following records:
id | cid | result
------------------
1 | C-1 | TAM
2 | C-1 | TAM
3 | C-2 | RAM
4 | C-2 | TAM
5 | C-3 | SAM
6 | C-3 | SAM
Now I want such cid's which has only one type of result, so the answer should be C-1 AND C-3 but not C-2 since it has two different type of results. Need Oracle query for this?
You simple need to understand GROUP BY and HAVING clause.
The answer is as simple as
select cid
from TEST_123
group by cid
having count(distinct result) = 1
Note group by selects the distinct keys from CID; the having filters on condition valid for all the records in the group, in your case count(distinct result) = 1
Use exists, its a little bit tricky cause every group result should be same
select t1.* from TEST_123 t1 where exists(
select 1 from TEST_123 t2 where t2.cid=t1.cid
and t2.result=t1.result
group by t2.cid,t2.result
having count(*)=
(select count(*) from TEST_123 t3
where t3.cid=t2.cid)
)
Exmple
with TEST_123 as
(
select 1 as id , 'c-1' as cid , 'tam' as result from dual
union all
select 2 as id , 'c-1' as cid , 'tam' as result from dual
union all
select 3 as id , 'c-2' as cid , 'tam' as result from dual
union all
select 4 as id , 'c-2' as cid , 'ram' as result from dual
)
select distinct t1.cid from TEST_123 t1 where exists(
select 1 from TEST_123 t2 where t2.cid=t1.cid
and t2.result=t1.result
group by t2.cid,t2.result
having count(*)=
(select count(*) from TEST_123 t3
where t3.cid=t2.cid)
)
demo
Based on #zaynul's answer, here is another variation:
with TEST_123 as
(
select 1 as id , 'c-1' as cid , 'tam' as result from dual
union all
select 2 as id , 'c-1' as cid , 'tam' as result from dual
union all
select 3 as id , 'c-2' as cid , 'tam' as result from dual
union all
select 4 as id , 'c-2' as cid , 'ram' as result from dual
)
select * from test_123 where cid in (
select cid from test_123 group by cid having count(distinct result) = 1);
select t.cid from
(select cid, count(*) as count from table_1 group by cid, result) t
group by t.cid
having count(*)=1;
Should work for you
I would use NOT EXISTS :
SELECT t.*
FROM table t
WHERE NOT EXISTS (SELECT 1 FROM table t1 WHERE t1.cid = t.cid AND t1.result <> t.result);

Fill NULL value with progressive row_number over partition function

What I have
From the following #MyTable I just have Name and Number columns.
My goal is to fill the valus where Number = NULL with a progressive number and get the values I have wrote into the Desidered_col column.
+------+--------+---------------+
| Name | Number | Desidered_col |
+------+--------+---------------+
| John | 1 | 1 |
| John | 2 | 2 |
| John | 3 | 3 |
| John | NULL | 4 |
| John | NULL | 5 |
| John | 6 | 6 |
| Mike | 1 | 1 |
| Mike | 2 | 2 |
| Mike | NULL | 3 |
| Mike | 4 | 4 |
| Mike | 5 | 5 |
| Mike | 6 | 6 |
+------+--------+---------------+
What I have tried
I have tried with the following query
SELECT Name, Number, row_number() OVER(PARTITION BY [Name] ORDER BY Number ASC) AS rn
FROM #MyTable
but it put all the NULL values first and then count the rows.
How can I fill the empty values?
Why I don't think is a duplicate question
I have read this question and this question but I don't think it is duplicate because they don't consider the PARTITION BY construct.
This is the script to create and populate the table
SELECT *
INTO #MyTable
FROM (
SELECT 'John' AS [Name], 1 AS [Number], 1 AS [Desidered_col] UNION ALL
SELECT 'John' AS [Name], 2 AS [Number], 2 AS [Desidered_col] UNION ALL
SELECT 'John' AS [Name], 3 AS [Number], 3 AS [Desidered_col] UNION ALL
SELECT 'John' AS [Name], NULL AS [Number], 4 AS [Desidered_col] UNION ALL
SELECT 'John' AS [Name], NULL AS [Number], 5 AS [Desidered_col] UNION ALL
SELECT 'John' AS [Name], 6 AS [Number], 6 AS [Desidered_col] UNION ALL
SELECT 'Mike' AS [Name], 1 AS [Number], 1 AS [Desidered_col] UNION ALL
SELECT 'Mike' AS [Name], 2 AS [Number], 2 AS [Desidered_col] UNION ALL
SELECT 'Mike' AS [Name], NULL AS [Number], 3 AS [Desidered_col] UNION ALL
SELECT 'Mike' AS [Name], 4 AS [Number], 4 AS [Desidered_col] UNION ALL
SELECT 'Mike' AS [Name], 5 AS [Number], 5 AS [Desidered_col] UNION ALL
SELECT 'Mike' AS [Name], 6 AS [Number], 6 AS [Desidered_col]
) A
This query is a bit complicated but seems to return your expected result. The only case it may be wrong is when someone does not have Number = 1.
The idea is that you must find gaps between numbers and count how many null values can be used to fill them.
Sample data
create table #myTable (
[Name] varchar(20)
, [Number] int
)
insert into #myTable
insert into #myTable
SELECT 'John' AS [Name], 1 AS [Number] UNION ALL
SELECT 'John' AS [Name], 2 AS [Number]UNION ALL
SELECT 'John' AS [Name], 3 AS [Number] UNION ALL
SELECT 'John' AS [Name], NULL AS [Number] UNION ALL
SELECT 'John' AS [Name], NULL AS [Number] UNION ALL
SELECT 'John' AS [Name], 6 AS [Number] UNION ALL
SELECT 'Mike' AS [Name], 1 AS [Number] UNION ALL
SELECT 'Mike' AS [Name], 2 AS [Number] UNION ALL
SELECT 'Mike' AS [Name], NULL AS [Number] UNION ALL
SELECT 'Mike' AS [Name], 4 AS [Number] UNION ALL
SELECT 'Mike' AS [Name], 5 AS [Number] UNION ALL
SELECT 'Mike' AS [Name], 6 AS [Number]
Query
;with gaps_between_numbers as (
select
t.Name, cnt = t.nextNum - t.Number - 1, dr = dense_rank() over (partition by t.Name order by t.Number)
, rn = row_number() over (partition by t.Name order by t.Number)
from (
select
Name, Number, nextNum = isnull(lead(Number) over (partition by Name order by number), Number + 1)
from
#myTable
where
Number is not null
) t
join master.dbo.spt_values v on t.nextNum - t.Number - 1 > v.number
where
t.nextNum - t.Number > 1
and v.type = 'P'
)
, ordering_nulls as (
select
t.Name, dr = isnull(q.dr, 2147483647)
from (
select
Name, rn = row_number() over (partition by Name order by (select 1))
from
#myTable
where
Number is null
) t
left join gaps_between_numbers q on t.Name = q.Name and t.rn = q.rn
)
, ordering_not_null_numbers as (
select
Name, Number, rn = dense_rank() over (partition by Name order by gr)
from (
select
Name, Number, gr = sum(lg) over (partition by Name order by Number)
from (
select
Name, Number, lg = iif(Number - lag(Number) over (partition by Name order by Number) = 1, 0, 1)
from
#myTable
where
Number is not null
) t
) t
)
select
Name, Number
, Desidered_col = row_number() over (partition by Name order by rn, isnull(Number, 2147483647))
from (
select * from ordering_not_null_numbers
union all
select Name, null, dr from ordering_nulls
) t
CTE gaps_between_numbers is seeking for numbers that are not consecutive. Number difference between current and next row shows how many NULL values can be used to fill the gaps. Then master.dbo.spt_values is used to multiply each row by that amount. In gaps_between_numbers dr column is gap number and cnt is amount of NULL values that need to used.
ordering_nulls orders only NULL values and is joined with CTE gaps_between_numbersto know in which position each row should appear.
ordering_not_null_numbers orders values that are not NULL. Consecutive Numbers will have same row number
And last step is to union CTE's ordering_not_null_numbers and ordering_nulls and make desired ordering
Rextester DEMO
In order to do this, you need a column that specifies the order of the rows in the table. You can do this using the identity() function:
SELECT identity(int, 1, 1) as MyTableId, a.*
INTO #MyTable
. . .
I'm pretty sure SQL Server will follow the ordering of a values() statement and in practice will follow the ordering of a union all. You can explicitly put this column in each row, if you prefer.
Then you can use this to assign your value:
select t.*,
row_number() over (partition by name order by mytableid) as desired_col
from #MyTable
You could also assign the new ranking based on Desidered_col using row_number() function with ORDER BY clause (select 1 or select null)
select *,
row_number() over (partition by Name order by (select 1)) New_Desidered_col
from #MyTable

tSQL UNPIVOT of comma concatenated column into multiple rows

I have a table that has a value column. The value could be one value or it could be multiple values separated with a comma:
id | assess_id | question_key | item_value
---+-----------+--------------+-----------
1 | 859 | Cust_A_1 | 1,5
2 | 859 | Cust_B_1 | 2
I need to unpivot the data based on the item_value to look like this:
id | assess_id | question_key | item_value
---+-----------+--------------+-----------
1 | 859 | Cust_A_1 | 1
1 | 859 | Cust_A_1 | 5
2 | 859 | Cust_B_1 | 2
How does one do that in tSQL on SQL Server 2012?
We have a user defined function that we use for stuff like this that we called "split_delimiter":
CREATE FUNCTION [dbo].[split_delimiter](#delimited_string VARCHAR(8000), #delimiter_type CHAR(1))
RETURNS TABLE AS
RETURN
WITH cte10(num) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
,cte100(num) AS
(
SELECT 1
FROM cte10 t1, cte10 t2
)
,cte10000(num) AS
(
SELECT 1
FROM cte100 t1, cte100 t2
)
,cte1(num) AS
(
SELECT TOP (ISNULL(DATALENGTH(#delimited_string),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM cte10000
)
,cte2(num) AS
(
SELECT 1
UNION ALL
SELECT t.num+1
FROM cte1 t
WHERE SUBSTRING(#delimited_string,t.num,1) = #delimiter_type
)
,cte3(num,[len]) AS
(
SELECT t.num
,ISNULL(NULLIF(CHARINDEX(#delimiter_type,#delimited_string,t.num),0)-t.num,8000)
FROM cte2 t
)
SELECT delimited_item_num = ROW_NUMBER() OVER(ORDER BY t.num)
,delimited_value = SUBSTRING(#delimited_string, t.num, t.[len])
FROM cte3 t;
GO
It will take a varchar value up to 8000 characters and will return a table with the delimited elements broken into rows. In your example, you'll want to use an outer apply to turn those delimited values into separate rows:
SELECT my_table.id, my_table.assess_id, question_key, my_table.delimited_items.item_value
FROM my_table
OUTER APPLY(
SELECT delimited_value AS item_value
FROM my_database.dbo.split_delimiter(my_table.item_value, ',')
) AS delimited_items

How to get the max version records?

I have a table like the following:
------------------------------------
Id FId UId Version
1 1 1 1
2 1 2 1
3 1 3 1
4 1 2 2
5 1 3 2
6 1 3 2
7 1 4 2
8 2 1 1
9 2 2 1
then I want the result to be:
--------------------------
FId UId Version
1 2 2
1 3 2
1 4 2
2 1 1
2 2 1
How to write the query based on the max 'Version' of each FId-UId pair?
The following gives the output requested.
select distinct t2.FId, t2.UId, t2.Version
from
(
select FId, max(Version) as "Version"
from MyTable
group by FId
) t1
inner join MyTable t2 on (t1.FId = t2.FId and t1.Version = t2.Version)
order by t2.FId, t2.UId
This will work on SQL 2005 and later:
DECLARE #t TABLE
(Id INT,
Fid INT,
[uid] INT,
[VERSION] INT
)
INSERT #t
SELECT 1,1,1,1
UNION ALL SELECT 2,1,2,1
UNION ALL SELECT 3,1,3,1
UNION ALL SELECT 4,1,2,2
UNION ALL SELECT 5,1,3,2
UNION ALL SELECT 6,1,3,2
UNION ALL SELECT 7,1,4,2
UNION ALL SELECT 8,2,1,1
UNION ALL SELECT 9,2,2,1
;WITH myCTE
AS
(
SELECT *,
RANK() OVER (PARTITION BY Fid
ORDER BY [VERSION] DESC
) AS rnk
FROM #t
)
SELECT DISTINCT Fid, [uid],[VERSION]
FROM myCTE
WHERE rnk = 1
ORDER BY Fid, [uid]
select FId, UId, Version
from MyTable
join (select Fid, Max(Version) as MaxVersion group by Fid) x
on x.FId = MyTable.FId and x.MaxVersion = MyTable.Version
Is the result you show correct- 1,3,2 should appear twice.If you need only once use select distinct
The foll query is working
with t as(
select 1 as id, 1 as fid , 1 as uid, 1 as version union all
select 2 , 1 , 2 , 1 union all
select 3 , 1 , 3 , 1 union all
select 4 , 1 , 2 , 2 union all
select 5 , 1 , 3 , 2 union all
select 6 , 1 , 3 , 2 union all
select 7 , 1 , 4 , 2 union all
select 8 , 2 , 1 , 1 union all
select 9 , 2 , 2 , 1)
select distinct t.fid,t.uid,t.version from t
inner join(
select fid,max(version) as maxversion from t
group by fid)as grp
on t.fid=grp.fid
and t.version=grp.maxversion