Conditional Deleting in SQL? - sql

See the SQL table below:
+------------+---------+
| Category | RevCode |
+------------+---------+
| 100.10.10 | 2 |
| 100.10.10 | 3 |
| 100.50.10 | 2 |
| 100.50.15 | 2 |
| 100.50.15 | 3 |
| 1000.80.10 | 3 |
| 200.10.10 | 3 |
| 200.50.10 | 3 |
| 200.80.10 | 3 |
| 2000.20.10 | 2 |
| 2000.20.10 | 3 |
| 2000.20.20 | 2 |
| 2000.20.20 | 3 |
| 2000.20.30 | 2 |
+------------+---------+
How can I delete all line items with the Rev Code of 3 where the following condition is met:
A Category has a Rev Code of both '2' and '3'.
For example:
+-----------+---------+
| Category | RevCode |
+-----------+---------+
| 100.10.10 | 2 |
| 100.10.10 | 3 |
+-----------+---------+
The above table will become:
+-----------+---------+
| Category | RevCode |
+-----------+---------+
| 100.10.10 | 2 |
+-----------+---------+

You can use sub_query with having clause like this:
delete from del_table
where RevCode = '3'
and Category in
(select Category from del_table
where RevCode in ('2','3')
group by Category
having count(distinct RevCode) =2 )
this statement may not be efficient, you can use exists clause instead of in clause.
Thanks for Charlesliam's comment. I use sql fiddle tested two cases below.
case1 :
create table del_table(Category varchar(20),RevCode Int);
INSERT INTO del_table VALUES
('100.10.10',2 ),
('100.10.10',3 ),
('100.50.10',2 ),
('100.50.15',3 )
result after deletion:
CATEGORY REVCODE
100.10.10 2
100.50.10 2
100.50.15 3
case2(a Category have more than two rows with duplicate RevCode) :
create table del_table(Category varchar(20),RevCode Int);
INSERT INTO del_table VALUES
('100.10.10',2 ),
('100.10.10',2 ),
('100.10.10',3 ),
('100.10.10',3 ),
('100.50.10',2 ),
('100.50.15',3 )
result after deletion:
CATEGORY REVCODE
100.10.10 2
100.10.10 2
100.50.10 2
100.50.15 3

See whether this helps you.
DECLARE #A TABLE (ID INT IDENTITY(1,1) PRIMARY KEY, CATEGORY VARCHAR(20),REVCODE INT)
INSERT INTO #A VALUES
('100.10.10',2 ),
('100.10.10',3 ),
('100.50.10',2 ),
('100.50.15',2 ),
('100.50.15',3 ),
('1000.80.10',3),
('200.10.10',3 ),
('200.50.10',3 ),
('200.80.10',3 ),
('2000.20.10',2),
('2000.20.10',3),
('2000.20.20',2),
('2000.20.20',3),
('2000.20.30',2)
SELECT * FROM #A
Table:
Query:
DELETE LU
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY CATEGORY ORDER BY REVCODE) ROW
FROM #A A
WHERE A.REVCODE IN (2,3)
) LU
WHERE LU.ROW = 2
SELECT * FROM #A
Result:

Related

SQL : join 3 tables and show all data from the 2 tables in sub query

I am trying to join three tables and show all the relevant data from both tables listed in the sub-query to get enough data so that I can perform a pivot later.
Listed below is an example of the 3 tables I am using.
"FileTable":
---------------------
|FileTable |
---------------------
| Id | Name |
---------------------
| 1 | File1.docx |
| 2 | File2.xlsx |
| 3 | File3.pdf |
---------------------
"TagTable":
---------------
|TagTable |
---------------
| Id | Name |
---------------
| 1 | Tag1 |
| 2 | Tag2 |
| 3 | Tag3 |
| 4 | Tag4 |
| 5 | Tag5 |
| 6 | Tag6 |
---------------
"TagValueTable":
------------------------------------
|TagValueTable |
------------------------------------
|FileId | TagId | Value |
------------------------------------
| 1 | 1 | file1tag1value |
| 1 | 2 | file1tag2value |
| 1 | 4 | file1tag4value |
| 2 | 1 | file2tag1value |
| 2 | 4 | file2tag4value |
------------------------------------
I am trying to write a query that will show me the Files, with ALL available Tags with the tag values for the files if they are provided.
I have written the following query but it only shows files and tags if there is a value in the "TagValue" table.
SELECT
f.Id AS 'FileId', f.[Name] AS 'FileName',
t.[Name] AS 'TagName',
tv.[Value] AS 'TagValue'
FROM
[FileTable] f
JOIN
TagValueTable tv ON tv.FileId = ISNULL(f.Id, tv.FileId)
JOIN
TagTable t ON t.Id = ISNULL(tv.TagId, t.Id)
ORDER BY
f.Id
I would like for the result to show up like below.
----------------------------------------------------------
|FileId |FileName |TagName |TagValue |
----------------------------------------------------------
| 1 |File1.doc |Tag1 |file1tag1value |
| 1 |File1.doc |Tag2 |file1tag2value |
| 1 |File1.doc |Tag3 |NULL |
| 1 |File1.doc |Tag4 |file1tag4vaule |
| 1 |File1.doc |Tag5 |NULL |
| 1 |File1.doc |Tag6 |NULL |
| 2 |File2.xls |Tag1 |file2tag1value |
| 2 |File2.xls |Tag2 |NULL |
| 2 |File2.xls |Tag3 |NULL |
| 2 |File2.xls |Tag4 |file2tag4vaule |
| 2 |File2.xls |Tag5 |NULL |
| 2 |File2.xls |Tag6 |NULL |
| 3 |File3.pdf |Tag1 |NULL |
| 3 |File3.pdf |Tag2 |NULL |
| 3 |File3.pdf |Tag3 |NULL |
| 3 |File3.pdf |Tag4 |NULL |
| 3 |File3.pdf |Tag5 |NULL |
| 3 |File3.pdf |Tag6 |NULL |
----------------------------------------------------------
The query needs to show all available tags for all files and their tag values. If the file doesn't have a tag value it should just be null. I am sure there is probably a better design for this but I am having to work with existing data so I am limited in the changes I can make.
I have tried rewriting the above query many different ways using different joins but I cannot seem to get it work. Also I am also doing this in a view and cannot use a stored procedure.
Any help is appreciated...
Thanks,
Robert
You can use a CROSS JOIN and an OUTER JOIN here instead of an INNER JOIN.
SELECT
f.Id as FileID,
f.Name AS FileName,
t.Name AS TagName,
tv.Value AS TagValue
FROM FileTable f
CROSS JOIN TagTable t
LEFT JOIN TagValueTable tv ON tv.FileId = f.Id
AND tv.TagId = t.Id
The CROSS JOIN will get you all permutations of FileTable and TagTable. The LEFT JOIN will bring in the tag values from the TagValueTable while keeping all of the results from the previous JOIN. An INNER JOIN, such as what you are using, will return only the records that match from both tables.
Here's a question that talks about the difference between an INNER and OUTER join.
What is the difference between "INNER JOIN" and "OUTER JOIN"?
select x.FileId, x.FileName, x.TagName, tv.Value
from (
select f.id FileId, f.name FileName, t.id TagId, t.Name TagName
from FileTable f, TagTable t
) x
left join TagValueTable tv on x.FileId = tv.FileId and x.TagId = tv.TagId
Since you want each of the Tags for the each file, you'll need to CROSS APPLY the Tags and then LEFT OUTER JOIN your link table to get the Value associated with the file and tag.
SETUP
CREATE TABLE FileTable ( Id int, Name varchar(20) ) ;
INSERT INTO FileTable (Id, Name)
VALUES ( 1, 'File1.docx' ), ( 2, 'File2.xlsx' ), ( 3, 'File3.pdf' ) ;
CREATE TABLE TagTable ( Id int, Name varchar(20) ) ;
INSERT INTO TagTable (Id, Name)
VALUES
( 1, 'Tag1' )
, ( 2, 'Tag2' )
, ( 3, 'Tag3' )
, ( 4, 'Tag4' )
, ( 5, 'Tag5' )
, ( 6, 'Tag6' )
;
CREATE TABLE TagValueTable ( FileId int, TagId int, theValue varchar(20) );
INSERT INTO TagValueTable (FileId, TagId, theValue)
VALUES
(1,1,'file1tag1value')
, (1,2,'file1tag2value')
, (1,4,'file1tag4value')
, (2,1,'file2tag1value')
, (2,4,'file2tag4value')
;
QUERY:
SELECT ft.Name, ca.TagName, tvt.theValue
FROM FileTable ft
CROSS APPLY (
SELECT tt.ID AS TagID, tt.Name AS TagName
FROM TagTable tt
) ca
LEFT OUTER JOIN TagValueTable tvt ON ft.ID = tvt.FileID
AND ca.TagID = tvt.TagID
GO
Name | TagName | theValue
:--------- | :------ | :-------------
File1.docx | Tag1 | file1tag1value
File1.docx | Tag2 | file1tag2value
File1.docx | Tag3 | null
File1.docx | Tag4 | file1tag4value
File1.docx | Tag5 | null
File1.docx | Tag6 | null
File2.xlsx | Tag1 | file2tag1value
File2.xlsx | Tag2 | null
File2.xlsx | Tag3 | null
File2.xlsx | Tag4 | file2tag4value
File2.xlsx | Tag5 | null
File2.xlsx | Tag6 | null
File3.pdf | Tag1 | null
File3.pdf | Tag2 | null
File3.pdf | Tag3 | null
File3.pdf | Tag4 | null
File3.pdf | Tag5 | null
File3.pdf | Tag6 | null
db<>fiddle here

Unable to write the SQL query as per my required output

I have two tables in MS Access database (Table1, Table2) with same columns namely Id, PartNo, Nomenclature, Quantity and Wksp. Id is the primary key in them. Id can be same in both tables. Now I want to write a query to get the output as shown under required output. How can I achieve this?
Table1
+----+--------+--------------+----------+----------+
| Id | PartNo | Nomenclature | Quantity | Wksp |
+----+--------+--------------+----------+----------+
| 1 | Part1 | Nomenc1 | 2 | Wksp1 |
| 2 | Part2 | Nomenc2 | 4 | Wksp1 |
| 3 | Part3 | Nomenc3 | 6 | Wksp1 |
| 4 | Part4 | Nomenc4 | 8 | Wksp1 |
+----+--------+--------------+----------+----------+
Table2
+----+--------+--------------+----------+-------+
| Id | PartNo | Nomenclature | Quantity | Wksp |
+----+--------+--------------+----------+-------+
| 1 | Part1 | Nomenc1 | 1 | Wksp2 |
| 2 | Part2 | Nomenc2 | 3 | Wksp2 |
| 3 | Part3 | Nomenc3 | 5 | Wksp2 |
| 4 | Part11 | Nomenc11 | 7 | Wksp2 |
| 5 | Part7 | Nomenc7 | 9 | Wksp2 |
+----+--------+--------------+----------+-------+
Required Output
+----+--------+--------------+-------+-------+
| Id | PartNo | Nomenclature | Wksp1 | Wksp2 |
+----+--------+--------------+-------+-------+
| 1 | Part1 | Nomenc1 | 2 | 1 |
| 2 | Part2 | Nomenc2 | 4 | 3 |
| 3 | Part3 | Nomenc3 | 6 | 5 |
| 4 | Part11 | Nomenc11 | 0 | 7 |
| 5 | Part7 | Nomenc7 | 0 | 9 |
| 6 | Part4 | Nomenc4 | 8 | 0 |
+----+--------+--------------+-------+-------+
First create a query to collect the IDs:
SELECT Id
FROM Table1
UNION
SELECT Id
FROM Tabel2;
Save this as, say, TBboth.
Then use this as source:
SELECT
TBboth.Id,
Nz(Table1!PartNo, Table2!PartNo) AS PartNo,
Nz(Table1!Nomenclature, Table2!Nomenclature) As Nomenclature,
Sum(Nz(Table1!Quantity,0)) AS Wksp1,
Sum(Nz(Table2!Quantity,0)) AS Wksp2
FROM
(TBboth
LEFT JOIN
TB1 ON TBboth.Id = TB1.Id)
LEFT JOIN
TB2 ON TBboth.Id = TB2.Id
GROUP BY
TBboth.Id,
Nz(Table1!PartNo, Table2!PartNo),
Nz(Table1!Nomenclature, Table2!Nomenclature);
Try this:
select Id , PartNo , Nomenclature, ISNULL(Wksp1,0) Wksp1, ISNULL(Wksp2,0) Wksp2
from
(
select Id , PartNo , Nomenclature ,Wksp, sum(Quantity) 'count' from Table1
group by Id , PartNo , Nomenclature,Wksp
union all
select Id , PartNo , Nomenclature ,Wksp, sum(Quantity) 'count' from Table2
group by Id , PartNo , Nomenclature,Wksp
) src
pivot
(
sum(count)
for Wksp in ([Wksp1], [Wksp2])
) P;
Try with This Method if you are using MS SQL Server
DECLARE #T1 TABLE
(
Id INT,
Part VARCHAR(20),
Qty INT,
Wksp VARCHAR(20) DEFAULT('Wksp 1')
)
DECLARE #T2 TABLE
(
Id INT,
Part VARCHAR(20),
Qty INT,
Wksp VARCHAR(20) DEFAULT('Wksp 2')
)
INSERT INTO #T1
VALUES(1,'Part1',10,'Wksp 1'),(2,'Part2',15,'Wksp 1'),(3,'Part3',12,'Wksp 1')
INSERT INTO #T1
VALUES(1,'Part1',10,'Wksp 2'),(2,'Part2',15,'Wksp 2'),(4,'Part4',20,'Wksp 2')
SELECT
Id = coalesce(a.id,b.id),
Part = coalesce(a.Part,b.Part),
Qty = coalesce(a.Qty,b.Qty),
Wksp =coalesce(a.Wksp,b.Wksp)
FROM #T1 A
FULL JOIN #T2 b
on a.Id = b.Id

Oracle update query using partition

I have following sample data in Oracle v.12 database
ID | NAME | DML_TYPE | FND_FILESEQNO | FND_FILERBA
---------------------------------------------------------
1 | name1a | insert | 1 | 1
1 | name1b | update | 1 | 2
2 | name2a | insert | 2 | 1
2 | name2b | update | 2 | 2
....
....
....
I want following 2 transactions to happen
delete old records (FND_FILESEQNO + FND_FILERBA) partition by 'ID' column
update latest record DML_TYPE = 'insert'
So eventually, if I query this table, I should get following result...
ID | NAME | DML_TYPE | FND_FILESEQNO | FND_FILERBA
---------------------------------------------------------
1 | name1b | insert | 1 | 2
2 | name2b | insert | 2 | 2
Many thanks
Try This:-
MERGE INTO STACTOVER a
USING ( SELECT * FROM (
SELECT STACTOVER.*,Row_Number() OVER(PARTITION BY ID ORDER BY ID)rn
FROM STACTOVER)WHERE rn>1
)b
ON
(a.ID = b.ID)
WHEN MATCHED THEN
UPDATE SET a.dml_type = 'insert'
DELETE WHERE a.NAME != b.NAME ;

Convert tuple value to column names

Got something like:
+-------+------+-------+
| count | id | grade |
+-------+------+-------+
| 1 | 0 | A |
| 2 | 0 | B |
| 1 | 1 | F |
| 3 | 1 | D |
| 5 | 2 | B |
| 1 | 2 | C |
I need:
+-----+---+----+---+---+---+
| id | A | B | C | D | F |
+-----+---+----+---+---+---+
| 0 | 1 | 2 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 | 1 | 1 |
| 2 | 0 | 5 | 1 | 0 | 0 |
I don't know if I can even do this. I can group by id but how would you read the count value for each grade column?
CREATE TABLE #MyTable(_count INT,id INT , grade VARCHAR(10))
INSERT INTO #MyTable( _count ,id , grade )
SELECT 1,0,'A' UNION ALL
SELECT 2,0,'B' UNION ALL
SELECT 1,1,'F' UNION ALL
SELECT 3,1,'D' UNION ALL
SELECT 5,2,'B' UNION ALL
SELECT 1,2,'C'
SELECT *
FROM
(
SELECT _count ,id ,grade
FROM #MyTable
)A
PIVOT
(
MAX(_count) FOR grade IN ([A],[B],[C],[D],[F])
)P
You need a "pivot" table or "cross-tabulation". You can use a combination of aggregation and CASE statements, or, more elegantly the crosstab() function provided by the additional module tablefunc. All basics here:
PostgreSQL Crosstab Query
Since not all keys in grade have values, you need the 2-parameter form. Like this:
SELECT * FROM crosstab(
'SELECT id, grade, count FROM table ORDER BY 1,2'
, $$SELECT unnest('{A,B,C,D,F}'::text[])$$
) ct(id text, "A" int, "B" int, "C" int, "D" int, "F" int);

ORACLE Not In on Multiple Columns

Kindly have a look at the following structure.
Table: A
+---------+----------+
| Col1A | Col2A |
+---------+----------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 1 | 2 |
| 2 | 2 |
+---------+----------+
Table: B
+---------+----------+
| Col1B | Col2B |
+---------+----------+
| 2 | 1 |
| 3 | 1 |
+---------+----------+
Here is what I am trying to achieve that is to get the result as following:
Result
+---------+----------+
| Col1A | Col2A |
+---------+----------+
| 1 | 1 |
| 4 | 1 |
| 1 | 2 |
| 2 | 2 |
+---------+----------+
What I want :
I want to get the result of table A records which are not exists in Table B. It should be on the basis of both column. If the combination exist in first table then don't no show it.
What I have tried so far :
The first approach that I have tried was to use Not In statement. Following is my statement.
SELECT A.COL1A, A.COL2A
FROM A
WHERE A.COL1A NOT IN (
SELECT B.COL1B FROM B
);
But the issue with this approach didn't consider the second column. It will give me the following result.
+---------+----------+
| 1 | 1 |
| 4 | 1 |
+---------+----------+
While it will not show following as it should be sub-traced because of we didn't checked for other column.
+---------+----------+
| 1 | 2 |
| 2 | 2 |
+---------+----------+
Then I tried Not Exists but I didn't worked too. Here is my query.
SELECT A.COL1A, A.COL2A
FROM A
WHERE NOT EXISTS(
SELECT B.COL1B,B.COL2B FROM B
);
Edit
Sorry I forget to include the fiddle link.
Here it is : Fiddle Demo
For using Exists I think you should use it like this:
SELECT *
FROM A
WHERE
NOT EXISTS(SELECT 1
FROM B
WHERE A.Col1A = B.Col1B AND A.Col2A = B.Col2B)
One way to do this which is conceptually simple is to JOIN tables A and B on the two columns, and then SELECT only those rows in A which do not exist in B:
SELECT *
FROM A
LEFT OUTER JOIN B
ON (A.COL1A = B.COL1B AND A.COL2A = B.COL2B)
WHERE B.COL1B IS NULL
SELECT *
FROM a
WHERE (col1a, col2a) NOT IN (SELECT col1b, col2b FROM b);
But be aware that this will fail if either col1b or col2b contains NULL values.
SQLFiddle DEMO
SELECT A.COL1A, A.COL2A
FROM A
WHERE (Col1A, Col2A) NOT IN (
SELECT Col1B, Col2B FROM B
);
You can even use:
SELECT A.COL1A, A.COL2A
FROM A
WHERE (Col1A, Col2A) NOT IN (
(2, 1),
(3, 1)
);