How to create a query to extract a value from my table - sql

I have an oracle table and i want to extract a value from column ,this my table
id|document_number|container_id|state|
--|---------------|------------|-----|
1 |CC330589 |356 | 40 |
--------------------------------------
1 |CC330589 |null | 99 |
-------------------------------------
I want to create a query that extract the container_id (value 356 ) whhere the document_number is 'CC330589' and the state is 99.
In my case I want the value 356 (not the null value).
Any idea how I can create the query ? Thanks in advance.

You can try below -
select * from tablename A
inner join
(select document_number, min(container_id ) from tablename group by document_number)B
on A.document_number=B.document_number
where A.document_number='CC330589' and state=99

You can use fid all not-null values for the given document and use EXISTS to ensure that state 99 exists for the given document:
SELECT *
FROM t
WHERE document_number = 'CC330589'
AND container_id IS NOT NULL
AND EXISTS (
SELECT 1
FROM t AS x
WHERE document_number = t.document_number
AND state = 99
)

This will get the result set you specify from the sample data. Whether it's the correct solution depends on your business rules which you have not specified:
select document_number
, max(container_id) as container_id
, max(state) as state
from your_table
group by document_number
Here is another solution
select t1.document_number
, coalesce(t1.container_id, t2.container_id) as container_id
, t1.state as state
from your_table t1
join your_table t2
on t1.document_number = t2.document_number
where t1.state = 99
and t1.document_number = 'CC330589'
and t2.state != 99
Again, this will produce the specified result from the sample data but may be incorrect against your real data set.

Related

Querying a subset

I want to write an SQL query to find records which contain a particular column and from that subset want to find records which doesn't contain a some other value. How do you write a query for that?
cid id2 attribute
--------------------------------
1 100 delete
1 100 payment
1 100 void
2 100 delete
2 102 payment
2 102 void
3 102 delete
3 103 payment
In above example, I want to list cid for which payment and delete attributes exist but void attribute doesn't exist. So it should list out 3 from above example because it doesn't have void attribute.
Forgot to mention that there could be more attributes. However, I need to list out records for which delete and payment exist regardless of other attributes but void doesn’t.
I call this a "set-within-sets" query, because you are looking for particular sets of attributes within each cid.
I would express this with group by and conditions in the having:
select cid
from t
group by cid
having sum(case when attribute = 'payment' then 1 else 0 end) > 0 and
sum(case when attribute = 'delete' then 1 else 0 end) > 0 and
sum(case when attribute = 'void' then 1 else 0 end) = 0 ;
In some databases, you can simplify this with string aggregation -- assuming there are no duplicate attributes for cids. For instance, using the MySQL function:
select cid
from t
where attribute in ('payment', 'delete' 'void')
group by cid
having group_concat(attribute order by attribute) = 'delete,payment';
You can use conditional aggregation:
select cid
from tablename
where attribute in ('delete', 'payment', 'void')
group by cid
having
count(distinct attribute) = 2
and
sum(
case attribute
when 'void' then 1
else 0
end
) = 0
If there are not more attributes than these 3, then you can omit the WHERE clause.
See the demo.
Results:
| cid |
| --- |
| 3 |
I'm assuming that there are only three attributes, so the logic behind this query is:
First COUNT the number of attributes GROUP BY cid, and then LEFT JOIN the original table ON attribute is void. You should grab cid that has exactly 2 attributes and no void.
The original table is named as temp:
SELECT
subq2.result_cid
FROM (
SELECT
*
FROM (
SELECT
T.cid AS result_cid,
COUNT(T.attribute) AS count
FROM
temp AS T
GROUP BY
T.cid
) AS subq
LEFT OUTER JOIN temp AS T2 ON subq.result_cid = T2.cid AND T2.attribute = 'void'
) AS subq2
WHERE subq2.count = 2 AND subq2.id2 IS NULL
use corelated subquery by using not exists
select t1.* from tablename t1
where not exists( select 1 from tablename t2
where t1.cid=t2.cid and attribute='void'
)
and exists ( select 1 from tablename t2
where t1.cid=t2.cid
having count(distinct attribute)=2
)
and attribute in ('payment','delete')
demo online

SQL to return if value is double

I have a question on how to validate if a row's doubled when other columns meet criteria.
The table looks looks like this:
Type Name ID Am
O Name1 1234 1
O Name1 1235 1
O Name1 4569 2
X Name2 1234 1
X Name2 4569 2
C name3 1234 1
For type O, I have under Name1, 2 ID's for the same Am = 1.
I'd like to to do a query that would check if multiples IDs can be found under same type & name & am values and return if >1, but ignore the rest of the types.
Thank you!
Try this
select type, name, am
from table
group by type, name, am
having count(*)>1
most dbms support row_number,so you can use this
select * from
(
select *, row_number() over(partition by Type,Name,Am order by ID) as rn
from t
) t1 where t1.rn>1
Are you trying to check if a combination of type/name has more than one am value?
If so:
select type, name
from t
group by type, name
having min(am) <> max(am);
I would use NOT EXISTS :
SELECT t.*
FROM table t
WHERE EXISTS (SELECT 1
FROM table t1
WHERE t1.Type = t.Type AND t1.Name = t.Name AND t1.Am = t.Am AND t1.ID <> t.ID
);

Find where two conditions are present in group

I have a table:
ref | name
===========
123abc | received
123abc | pending
134b | pending
156c | received
I want to be able to identify instances where a ref only has a pending and not a received. Note there could be multiple receives and pendings for the same ref.
How can I output the ref's that only have a pending and not a received?
So in my example, it would return:
134b | pending
I think it's something like:
SELECT ref, name FROM my_table
WHERE ref IS NOT NULL
GROUP BY ref, name
HAVING ref = 'pending' AND ref = 'received'
;
I would use aggregation:
select name
from my_table
where ref in ('pending', 'received')
group by name
having min(ref) = 'pending' and min(ref) = max(ref);
The second condition comparing min and max is, strictly speaking, not necessary. But it eliminates the dependence on the alphabetical ordering of the values.
You can use not exists for what you need (btw, from your data, column "name" contains values like pending and received):
select distinct ref, name
from my_table t1
where t1.name = 'pending' and not exists (select * from my_table t2 where t1.ref=t2.ref and t2.name='received')
PS. You can validate here with your sample data and my query:
https://dbfiddle.uk/?rdbms=postgres_10&fiddle=6fd633fe52129ff3246d8dba55e5fc17
Another way of doing it is with a WITH statement. This way, there is no need for nested sub-queries.
WITH ref_recieved_pending AS (
SELECT
ref,
sum(CASE WHEN name = 'received'
THEN 1
ELSE 0 END) as recieved_count,
sum(CASE WHEN name = 'pending'
THEN 1
ELSE 0 END) as pending_count
FROM test_table_2
GROUP BY ref
)
SELECT DISTINCT
ref,
'pending' as pending
FROM ref_recieved_pending
WHERE pending_count > 0 AND recieved_count = 0;

Trying to find unique records in a table that don't have a negating record

I have a table with a whole bunch of records.
table looks like this (simplified):
ID DoID DoQty DoType DoValue
1 17 1 Door 15
2 17 -1 Door -15
3 18 1 Window 75
4 19 1 Bed 125
5 19 1 Bed 134
so this is what I'd like to pull
ID DoId DoQty DoType DoValue
3 18 1 WIndows 75
4 19 1 Bed 125
5 19 1 Bed 134
I don't need DoID=17 because it has a 2nd line where DoQty is -1. SO that overall DoQty = 0. I only need records where there isn't a DoQty=-1. The problem here is that I do not want to group by DoID I want to be able to see the whole record line (no group by)
EDIT:
Unfortunately I might not have explained my question correctly. Basically, if I run the following query, i get the correct counts, however my goal is to get the details of each line.
SELECT t.DoID,
'Available' = Sum(t.DoQty)
From t
GROUP BY t.DoID
This gives me grouped results from which I can't do anything with.
As i understand you dont what to record where has any negative DoQty in any row. If it is correct, a possible solution is below,
SELECT t1.ID, t1.DoID, t1.DoQty, t1.DoType, t1.DoValue
FROM table t1
LEFT JOIN table t2 ON t2.DoQty < 0 AND t1.DoID = t2.DoID
WHERE t2.DoID IS NULL
We can use conditional aggregation here:
WITH cte AS (
SELECT DoID
FROM yourTable
GROUP BY DoID
HAVING SUM(CASE WHEN DoQty < 0 THEN 1 ELSE 0 END) = 0
)
SELECT *
FROM yourTable
WHERE DoID IN (SELECT DoID FROM cte);
This would return every DoID whose group of records does not have any DoQty values which are negative.
Is this what you want?
select t.*
from t
where not exists (select 1 from t t2 where t2.doid = t.doid and t2.doqty = - t.doqty);
This filters out rows where the "negative" value exists.
What I'm trying to do here is only pull up DoID's that don't have have a DoQty>0
Try this...
SELECT DoID
FROM table tbl
WHERE NOT EXISTS (
SELECT DoID FROM table WHERE DoQty <= 0 AND DoID = tbl.DoID
);
You can try the below:
With Agg AS
(
select DOID,sum(DoQty) as Qty from TableName
group by DOID having sum(DoQty)>0
)
select T.* from TableName T Inner Join Agg A
on T.DOID=A.DOID;
You seems want :
SELECT *, SUM(t.DoQty) OVER (PARTITION BY t.DoID) as Available
From t;

Replace NULL with values

Here is my challenge:
I have a log table which every time a record is changed adds a new record but puts a NULL value for each non-changed value in each record. In other words only the changed value is set, the rest unchanged fields in each row simply has a NULL value.
Now I would like to replace each NULL value with the value above it that is NOT a NULL value like below:
Source table: Task_log
ID Owner Status Flag
1 Bob Registrar T
2 Sue NULL NULL
3 NULL NULL F
4 Frank Admission T
5 NULL NULL F
6 NULL NULL T
Desired output table: Task_log
ID Owner Status Flag
1 Bob Registrar T
2 Sue Registrar T
3 Sue Registrar F
4 Frank Admission T
5 Frank Admission F
6 Frank Admission T
How do I write a query which will generate the desired output table?
One the new windowed function of SQLServer 2012 is FIRST_VALUE, wich have quite a direct name, it can be partitioned through the OVER clause, before using it is necessary to divide every column in data block, a block for a column begin when a value is found.
With Block As (
Select ID
, Owner
, OBlockID = SUM(Case When Owner Is Null Then 0 Else 1 End)
OVER (ORDER BY ID)
, Status
, SBlockID = SUM(Case When Status Is Null Then 0 Else 1 End)
OVER (ORDER BY ID)
, Flag
, FBlockID = SUM(Case When Flag Is Null Then 0 Else 1 End)
OVER (ORDER BY ID)
From Task_log
)
Select ID
, Owner = FIRST_VALUE(Owner) OVER (PARTITION BY OBlockID ORDER BY ID)
, Status = FIRST_VALUE(Status) OVER (PARTITION BY SBlockID ORDER BY ID)
, Flag = FIRST_VALUE(Flag) OVER (PARTITION BY FBlockID ORDER BY ID)
FROM Block
SQLFiddle demo
The UPDATE query is easily derived
As I mentioned in my comment, I would try to fix the process that is creating the records rather than fixing the junk data. If that is not an option, the code below should get you pointed in the right direction.
UPDATE t1
set t1.owner = COALESCE(t1.owner, t2.owner),
t1.Status = COALESCE(t1.status, t2.status),
t1.Flag = COALESCE(t1.flag, t2.flag)
FROM Task_log as t1
INNER JOIN Task_log as t2
ON t1.id = (t1.id + 1)
where t1.owner is null
OR t1.status is null
OR t1.flag is null
I can think of several approaches.
You could use a combination of COALESCE with an array aggregate function. Unfortunately it doesn't look like SQL Server supports array_agg natively (although some nice people have developed some workarounds).
You could also use a subselect for each column.
SELECT id,
(SELECT TOP 1 FROM (SELECT owner FROM ... WHERE id = outer_id AND owner IS NOT NULL order by ID desc )) AS owner,
-- other columns
You could probably do something with window functions, too.
A vanilla solution would be:
select id
, owner
, coalesce(owner, ( select owner from t t2
where id = (select max(id) from t t3
where id < t1.id and owner is not null))
) as new_owner
, flag
, coalesce(flag, ( select flag from t t2
where id = (select max(id) from t t3
where id < t1.id and flag is not null))
) as new_flag
from t t1
Rather inefficient, but should work on most DBMS