Find groups that do not contain any NULL value - sql

I have a many to many table called dbo.ObjectOwner having following columns:
ObjectId | OwnerId | StartDate |EndDate
Where ObjectId, OwnerId is not a primary key and Startdate and Enddate refer to the dates where Object is owned by Owner.
The query I'm trying to do should return all
ObjectId's where for each ObjectId, it has no associated records where EndDate is null. I.e, return all objects who currently have no owner.
so something like
foreach(objectId in dbo.ObjectOwner)
if (
doesnotexist (records where ObjectId = objectid and EndDate is null)
)
{
add this objectid to the select table
}
I had a look at group by and having, but the following script returns all records
SELECT oo.ObjectId
FROM dbo.ObjectOwner oo
GROUP BY oo.ObjectId
HAVING NOT EXISTS (
SELECT 1
FROM dbo.ObjectOwner
WHERE dbo.ObjectOwner.EndDate = null
)
Thanks in advance

You can use GROUP BY and HAVING. The following works because NULL values are not COUNTed:
SELECT ObjectId
FROM ObjectOwner
GROUP BY ObjectId
HAVING COUNT(*) = COUNT(EndDate)

It can't work if you write <...> = NULL, because NULL can't be equal to something.
SELECT oo.ObjectId
FROM dbo.ObjectOwner oo
GROUP BY oo.ObjectId
HAVING NOT EXISTS (
SELECT 1
FROM dbo.ObjectOwner
WHERE dbo.ObjectOwner.EndDate IS NULL
)

Related

Query to determine cumulative changes to records

Given the following table containing the example rows, I’m looking for a query to give me the aggregate results of changes made to the same record. All changes are made against a base record in another table (results table), so the contents of the results table are not cumulative.
Base Records (from which all changes are made)
Edited Columns highlighted
I’m looking for a query that would give me the cumulative changes (in order by date). This would be the resulting rows:
Any help appreciated!
UPDATE---------------
Let me offer some clarification. The records being edited exist in one table, let's call that [dbo].[Base]. When a person updates a record from [dbo].[Base], his updates go into [dbo].[Updates]. Therefore, a person is always editing from the base table.
At some point, let's say once a day, we need to calculate the sum of changes with the following rule:
For any given record, determine the latest change for each column and take the latest change. If no change was made to a column, take the value from [dbo].[Base]. So, one way of looking at the [dbo].[Updates] table would be to see only the changed columns.
Please let's not discuss the merits of this approach, I realize it's strange. I just need to figure out how to determine the final state of each record.
Thanks!
This is dirty, but you can give this a shot (test here: https://rextester.com/MKSBU15593)
I use a CTE to do an initial CROSS JOIN of the Base and Update tables and then a second to filter it to only the rows where the IDs match. From there I use FIRST_VALUE() for each column, partitioned by the ID value and ordered by a CASE expression (if the Base column value matches the Update column value then 1 else 0) and the Datemodified column to get the most recent version of the each column.
It spits out
CREATE TABLE Base
(
ID INT
,FNAME VARCHAR(100)
,LNAME VARCHAR(100)
,ADDRESS VARCHAR(100)
,RATING INT
,[TYPE] VARCHAR(5)
,SUBTYPE VARCHAR(5)
);
INSERT INTO dbo.Base
VALUES
( 100,'John','Doe','123 First',3,'Emp','W2'),
( 200,'Jane','Smith','Wacker Dr.',2,'Emp','W2');
CREATE TABLE Updates
(
ID INT
,DATEMODIFIED DATE
,FNAME VARCHAR(100)
,LNAME VARCHAR(100)
,ADDRESS VARCHAR(100)
,RATING INT
,[TYPE] VARCHAR(5)
,SUBTYPE VARCHAR(5)
);
INSERT INTO dbo.Updates
VALUES
( 100,'1/15/2019','John','Doe','123 First St.',3,'Emp','W2'),
( 200,'1/15/2019','Jane','Smyth','Wacker Dr.',2,'Emp','W2'),
( 100,'1/17/2019','Johnny','Doe','123 First',3,'Emp','W2'),
( 200,'1/19/2019','Jane','Smith','2 Wacker Dr.',2,'Emp','W2'),
( 100,'1/20/2019','Jon','Doe','123 First',3,'Cont','W2');
WITH merged AS
(
SELECT b.ID AS IDOrigin
,'1/1/1900' AS DATEMODIFIEDOrigin
,b.FNAME AS FNAMEOrigin
,b.LNAME AS LNAMEOrigin
,b.ADDRESS AS ADDRESSOrigin
,b.RATING AS RATINGOrigin
,b.[TYPE] AS TYPEOrigin
,b.SUBTYPE AS SUBTYPEOrigin
,u.*
FROM base b
CROSS JOIN
dbo.Updates u
), filtered AS
(
SELECT *
FROM merged
WHERE IDOrigin = ID
)
SELECT distinct
ID
,FNAME = FIRST_VALUE(FNAME) OVER (PARTITION BY ID ORDER BY CASE WHEN FNAME = FNAMEOrigin THEN 1 ELSE 0 end, datemodified desc)
,LNAME = FIRST_VALUE(LNAME) OVER (PARTITION BY ID ORDER BY CASE WHEN LNAME = LNAMEOrigin THEN 1 ELSE 0 end, datemodified desc)
,ADDRESS = FIRST_VALUE(ADDRESS) OVER (PARTITION BY ID ORDER BY CASE WHEN ADDRESS = ADDRESSOrigin THEN 1 ELSE 0 end, datemodified desc)
,RATING = FIRST_VALUE(RATING) OVER (PARTITION BY ID ORDER BY CASE WHEN RATING = RATINGOrigin THEN 1 ELSE 0 end, datemodified desc)
,[TYPE] = FIRST_VALUE([TYPE]) OVER (PARTITION BY ID ORDER BY CASE WHEN [TYPE] = TYPEOrigin THEN 1 ELSE 0 end, datemodified desc)
,SUBTYPE = FIRST_VALUE(SUBTYPE) OVER (PARTITION BY ID ORDER BY CASE WHEN SUBTYPE = SUBTYPEOrigin THEN 1 ELSE 0 end, datemodified desc)
FROM filtered
Don't you just want the last record?
select e.*
from edited e
where e.datemodified = (select max(e2.datemodified)
from edited e2
where e2.id = e.id
);

How to fill Joining date and id based on following requirement?

I want to fill the joining date and id by creating a new view and output should be like second image
you might be looking for something like:
UPDATE mytable
SET tofill.ID = fillvalues.ID
,tofill.JOININGDATE = fillvalues.JOININGDATE
FROM mytable tofill
INNER JOIN
( SELECT DISTINCT ID, JOININGDATE, NAME
FROM mytable
WHERE ID IS NOT NULL
AND JOININGDATE IS NOT NULL
) fillvalues
ON tofill.NAME = fillvalues.NAME
WHERE tofill.ID IS NULL
OR tofill.JOININGDATE IS NULL
;
I am not familiar with Oracle, but statement should be teh same or similiar

Find distinct values from an inner query

How can I fetch distinct values from an inner query?
Scenario:
I have a table: MyData with columns ID and Starttime.
ID is a hex string
and starttime is a time stamp.
ID and starttime can be a null.
Here is how the table looks:
ID StartTime
01655a70 2014-10-24 06:22:03.0
01655a70 2014-10-24 06:22:03.0
b752 2014-10-15 03:19:03.0
b752 <null>
3922b 2014-10-15 03:19:03.0
d98cb <null>
I want to get distinct ID values that do not have any NULL value in it's starttime column.
Expected result should be:
01655a70
3922b
I have tried:
select distinct(ID) from Mydata where ID in (select ID from MyData where id not like '' and starttime is not null)
select distinct(inner.ID) from (select ID from MyData where id not like '' and starttime is not null) as inner
which seems to yield all the ID entries including the ones that have a null value.
Also looked at the SO posts:
http://stackoverflow.com/questions/23278387/options-for-returning-distinct-values-across-an-inner-join
and
http://stackoverflow.com/questions/13149857/select-distinct-on-inner-join-query-filtering-by-multiple-values-on-a-single-col
The select distinct query seems straight forward to me, is there something obviously wrong here?
Additional Information:
My DB is a MS Access DB, *.accdb type database.
select t.id from (
select id, count(*) as n_all,
count(starttime) as n_time
from Mydata
group by id
) t
where t.n_all = t.n_time;
count(*) counts all rows
count(col) counts not null col values
Another option:
select distinct m1.id from Mydata m1
where not exists (select 1 from Mydata m2 where m2.id = m1.id and m2.starttime is null);
Your query:
select distinct(ID) from Mydata
where ID in (select ID from MyData
where id not like '' and starttime is not null);
id not like '' this condition doesn't test for null. Use id is not null instead
The subquery just returns all the ids which have not null starttime. So your query doesn't check all the values of starttime for each id and it is equivalent to:
select distinct ID from MyData where id not like '' and starttime is not null;
The second query does the same thing as the first query - you just added an alias for your subquery.

Returning distinct prioritizing results with order by

Name varchar, Value int, Active bit
-----------------------------------
'Name1',1,1
'Name2',2,1
'Name1',3,0
'Name2',4,0
'Name3',1,1
'Name4',1,1
I want to return where Active is anything but prioritize when it's 0 so I want to return this:
'Name1',3
'Name2',4
'Name3',1
'Name4',1
I tried this, but get an error to include Active in my return statement
Select Distinct Name, Value From Table Order by Active
So I tried this:
Select Distinct Name, Value, Active From Table Order by Active
But now it returns all the rows. I would like to prioritize where Active = 0 in the distinct results but since it requires I put Active in the return statement makes this complicated.
Can someone help?
Your question is a little confusing, but if I'm understanding it correctly, you need to use a group by statement:
select name,
max(case when active = 0 then value end) value
from yourtable
group by name
SQL Fiddle Demo
With your edits, you can use coalesce and still get it to work:
select name, coalesce(max(case when active = 0 then value end), max(value)) value
from yourtable
group by name
More Fiddle
You can order by fields not contained in the select clause
Select Name, Value
From Table
ORDER BY Active, Name, Value
But you cannot use SELECT DISTINCT at the same time.
If you use "select distinct" there is the possibility that some rows will be discarded, when this happens there is no longer any viable relationship retained between [Active] and the "distinct" rows. So if using select distinct, and you need to order by [Active], then [Active] MUST be in the select clause.
I couldn't delete the post b/c of the other answers, but here is answer I was looking for in case anyone else was wondering.
SELECT Distinct Name,Value FROM Table WHERE Active = 0
UNION ALL
SELECT Distinct Name,Value FROM Table a WHERE Active = 1 AND NOT EXISTS (
SELECT TOP 1 1 FROM Table a2 WHERE a2.Active = 0 AND a2.Name = a.Name
)
Review #Sgeddes 's answer for a better solution.
Thanks to everyone for their help.
Perhaps this:
create table #t(
Active int not null,
Name varchar(10) not null,
Value int not null,
primary key clustered (Active desc,Name,Value)
);
insert #t(Active,Name,Value)
select Active,Name,Value from [Table];
select Name, Value
from #t;
go
yields as desired:
Name Value
---------- -----------
Name1 1
Name2 2
Name3 1
Name4 1
Name1 3
Name2 4

SQL query to get records even if count is 0 in one table

select id, name, 'First Category' as category, count(id) as totalCalls
from missed_call
where name = 'whatever1'
group by name, category
UNION
select id, name, 'Second Category' as category, count(id) as totalCalls
from missed_call
where name = 'whatever2'
group by name, category
order by name ASC, totalCalls DESC
The previous query will not retrieve the records where totalCalls is 0.
So, how can I do to get those records and present totalCalls as 0?
UPDATE: I have tried changing count(id) as totalCalls for IFNULL(count(id), 0) as totalCalls but it doesn't solve the problem. Perhaps, because count(id) is actually not null, it just does not exist.
If you are unwilling to expand your database schema you can always pretend there is a table:
select surrogateTable.name,
surrogateTable.Category,
count(id) as totalCalls
from
(
select 'whatever1' Name,
'First Category' Category
union all
select 'whatever2',
'Second Category'
) surrogateTable
left join missed_call
on surrogateTable.Name = missed_call.Name
group by surrogateTable.name, surrogateTable.category
I dropped id in select because you should not select something you are not grouping on - this is probably MySql.
Check this on Sql Fiddle.
Your problem is that you only look at missed calls and not at categories, so you cannot notice categories that have no corresponding missed calls.
Here is the skeleton that will do that, supposing you will adapt it to the real structure of the category table.
SELECT ...
FROM Category cat
LEFT JOIN missed_call call ON call.category = category.id
WHERE (call.name = 'whatever1' OR call.category IS NULL)
GROUP BY call.name, call.category
...
Note especially call.category IS NULL. The column is supposedly not nullable; so this really checks for a Category row without any corresponding calls, an artifact of the outer join.
You should define a table named category to contain a complete list of all category names that are possible, even those that have no calls assigned to them (i.e. zero).
create table category
(
id numeric(10,0) NOT NULL,
name varchar(10) NULL
)
Then you can query the full list of categories from this table and LEFT JOIN the results against what you have from above.
You can then amend your missed_call to use foreign keys against your new category table for better efficiency and better schema design
create table missed_call
(
id numeric(10,0) NOT NULL,
first_category_id numeric(10,0) NULL,
second_category_id numeric(10,0) NULL,
name varchar(12)
)