Select the last non-NULL value when current row is NULL - sql

I know that there are a lot of solutions for this but unfortunately I cannot use partition or keyword TOP. Nothing I tried on earlier posts works.
My table looks like this:
The result I want is when any completion percentage is NULL it should get the value from last non-value completion percentage, like this:
I tried this query but nothing works. Can you tell me where I am going wrong?
SELECT sequence,project_for_lookup,
CASE WHEN completion_percentage IS NOT NULL THEN completion_percentage
ELSE
(SELECT max(completion_percentage) FROM [project_completion_percentage] AS t2
WHERE t1.project_for_lookup=t2.project_for_lookup and
t1.sequence<t2.sequence and
t2.completion_percentage IS NOT null
END
FROM [project_completion_percentage] AS t1

SQL Server 2008 doesn't support cumulative window functions. So, I would suggest outer apply:
select cp.projectname, cp.sequence,
coalesce(cp.completion_percentage, cp2.completion_percentage) as completion_percentage
from completion_percentage cp outer apply
(select top 1 cp2.*
from completion_percentage cp2
where cp2.projectname = cp.projectname and
cp2.sequence < cp.sequence and
cp2.completion_percentage is not null
order by cp2.sequence desc
) cp2;

Does this work? It seems to for me. You were missing a parenthesis and had the sequence backwards.
http://sqlfiddle.com/#!3/465f2/4
SELECT sequence,project_for_lookup,
CASE WHEN completion_percentage IS NOT NULL THEN completion_percentage
ELSE
(
SELECT max(completion_percentage)
FROM [project_completion_percentage] AS t2
WHERE t1.project_for_lookup=t2.project_for_lookup
-- sequence was reversed. You're on the row t1, and want t2 that is from a prior sequence.
and t2.sequence<t1.sequence
and t2.completion_percentage IS NOT null
--missing a closing paren
)
END
FROM [project_completion_percentage] AS t1

Related

postgres: COUNT, DISTINCT is not implemented for window functions

I am trying to use COUNT(DISTINC column) OVER(PARTITION BY column) when I am using COUNT + window function(OVER).
I get an error like the one in the title and can't get it to work.
I have looked into how to deal with this error, but I have not found an example of how to deal with such a complex query as the one below.
I cannot find an example of how to deal with such a complex query as shown below, and I am not sure how to handle it.
The COUNT part of the problem exists on line 65.
How can such a complex query be resolved without slowing down?
WITH RECURSIVE "cte" AS((
SELECT
"videos_productvideocomment"."id",
"videos_productvideocomment"."user_id",
"videos_productvideocomment"."video_id",
"videos_productvideocomment"."parent_id",
"videos_productvideocomment"."text",
"videos_productvideocomment"."commented_at",
"videos_productvideocomment"."edited_at",
"videos_productvideocomment"."created_at",
"videos_productvideocomment"."updated_at",
"videos_productvideocomment"."id" AS "root_id"
FROM
"videos_productvideocomment"
WHERE
(
"videos_productvideocomment"."parent_id" IS NULL
AND "videos_productvideocomment"."video_id" = 'f264433c-c0af-49cc-8b40-84453da71b2d'
)
) UNION(
SELECT
"videos_productvideocomment"."id",
"videos_productvideocomment"."user_id",
"videos_productvideocomment"."video_id",
"videos_productvideocomment"."parent_id",
"videos_productvideocomment"."text",
"videos_productvideocomment"."commented_at",
"videos_productvideocomment"."edited_at",
"videos_productvideocomment"."created_at",
"videos_productvideocomment"."updated_at",
"cte"."root_id" AS "root_id"
FROM
"videos_productvideocomment"
INNER JOIN
"cte"
ON "videos_productvideocomment"."parent_id" = "cte"."id"
))
SELECT
*,
EXISTS(
SELECT
(1) AS "a"
FROM
"videos_productvideolikecomment" U0
WHERE
(
U0."comment_id" = t."id"
AND U0."user_id" = '3bd3bc86-0335-481e-9fd2-eb2fb1168f48'
)
LIMIT 1
) AS "liked"
FROM
(
SELECT DISTINCT
"cte"."id",
"cte"."created_at",
"cte"."updated_at",
"cte"."user_id",
"cte"."text",
"cte"."commented_at",
"cte"."edited_at",
"cte"."parent_id",
"cte"."video_id",
"cte"."root_id" AS "root_id",
COUNT(DISTINCT "cte"."root_id") OVER(PARTITION BY "cte"."root_id") AS "reply_count", <--- here
COUNT("videos_productvideolikecomment"."id") OVER(PARTITION BY "cte"."id") AS "liked_count"
FROM
"cte"
LEFT OUTER JOIN
"videos_productvideolikecomment"
ON (
"cte"."id" = "videos_productvideolikecomment"."comment_id"
)
) t
WHERE
t."id" = t."root_id"
ORDER BY
CASE
WHEN t."user_id" = '3bd3bc86-0335-481e-9fd2-eb2fb1168f48' THEN 0
ELSE 1
END ASC,
"liked_count" DESC
DISTINCT will look for duplicates and remove it, but in big data it will take a lot of time to process this query, you should process the middle of the record in the programming part I think it will be fast than. Thank

How to filter out a "NULL" valued line if there's another line that is otherwise identical but doesn't have a "NULL" value?

As you can see from the image. Lines 5 and 6 are otherwise identical but line 5 has "NULL" value. So I want to filter line 5 out but leave line 6.
Then there are also single lines that have "NULL" value but I also want to keep those. So how can I do this? How can I keep the single/unique lines that have "NULL" value and simultaneously filter out lines that have an identical counterpart but the counterpart has a "NULL" value?
I've tried to use coalesce group by -function but haven't got the wanted results yet.
You can use not exists:
select t.*
from mytable t
where
date_updated is not null
or not exists (
select 1
from mytable t1
where
t1.state = t.state
and t1.foo = t.foo
and t1.date_created = t.date_created
and t1 date_created is not null
)
Another option is window functions, if your database supports them:
select *
from (
select
t.*,
max(date_updated) over(partition by state, foo, date_created) max_date_updated
from mytable t
) t
where date_updated is not null or max_date_updated is null

No records found when running not in operator

I am trying to get records from one table excluding some records (Order No.'s in the Union). Can anybody tell me what could be wrong with this query. I am getting no records after running it.
SELECT *
FROM [dbo].[FMD15_18]
WHERE [OrderNo] NOT IN ((SELECT OrderNo
FROM [dbo].[FMD15_18]
WHERE [Item Description] Like '%AP%')
UNION ALL
SELECT [OrderNo] FROM [dbo].[AP&C]
)
I would use NOT EXISTS instead :
SELECT t.*
FROM [dbo].[FMD15_18] t
WHERE NOT EXISTS (SELECT 1
FROM [dbo].[FMD15_18] t1
WHERE t1.OrderNo = t.OrderNo AND
t1.[Item Description] Like '%AP%') AND
NOT EXISTS (SELECT 1
FROM [dbo].[AP&C] a
WHERE a.OrderNo = t.OrderNo);
However, i suspect some nullable issues with current query. If so, then you need to fiter out with IS NOT NULL in subquery.
NOT IN is tricky. I guess that OrderNo is nullable that is why you don't get any rows.
SELECT *
FROM [dbo].[FMD15_18]
WHERE [OrderNo] NOT IN (SELECT COALESCE(OrderNo, '^')
FROM [dbo].[FMD15_18]
WHERE [Item Description] Like '%AP%'
UNION ALL
SELECT COALESCE([OrderNo], '^') FROM [dbo].[AP&C]
);
Explanation:
1 IN (1, NULL)
<=>
1=1 OR 1 = NULL
-- 1 row returned
And NOT NULL:
1 NOT IN (1, NULL)
1!=1 AND 1 != NULL
-- always not true
-- always 0 rows returned
You should be able to avoid using sub-queries entirely. It sounds like you want orders (from FMD15_18) where the description does not contain "AP", and the order number is not in the AP&C table. If that's the case, you could do something like the following:
select FMD15_18.*
from FMD15_18
left join [AP&C] on
[AP&C].OrderNo = FMD15_18.OrderNo
where
FMD15_18.[Item Description] NOT like '%AP%'
and [AP&C].OrderNo is null
I don't know what kind of data is in the [FMD15_18].[Item Description] field, but it seems heavy-handed to exclude items where the description contains 2 letters. How long does the description column tend to be? Might there be records that contain "AP" that you're excluding inadvertently? Items with descriptions as varied as "APPLE", "MAPLE SYRUP", and "BURLAP" would be excluded based on this condition.

Oracle SQL re-use subquery "WITH" clause assistance

I have an SQL query in which I need to take the output of a subquery and use it more than once. My existing query works, but only if I repeat the subquery each time I need it. Unfortunately the subquery is complex, and takes time to execute - meaning that multiple iterations really slow the whole thing down.
I have read that you can use the "WITH" statement to assign a subquery output to a variable, in order to re-use that variable. However the problem I'm having is that within the subquery, I need to reference values from the main query. And it appears that if I use WITH - before the main query SELECT - then those references are not recognised. I'll give you a simplified example:
WITH
DateX AS
(
SELECT
MAX(TableSub.Date)
FROM
TableA TableSub
WHERE
TableSub.ID = TableMain.ID
AND TableSub.Event = 'AnotherEvent'
AND TableSub.Date BETWEEN '01-Jan-2015' AND '31-Dec-2015'
)
SELECT
TableMain.ID
FROM
TableA TableMain
WHERE
TableMain.Event = 'MainEvent'
AND TableMain.Date >= DateX
AND (
SELECT
TableSub2.ID
FROM
TableA TableSub2
WHERE
TableSub2.ID = TableMain.ID
TableSub2.Event = 'ThirdEvent'
AND TableSub2.Date <= DateX
) IS NULL
I hope this is clear. It's a simplified version of what I have, but you can see that DateX is used in more than one place: within the main query, and within a subquery. However the problem is that when DateX is defined by WITH, I need to link the ID back to the ID of the main query. And it's not working...
I would be grateful for any advice on this. Am I doing it wrong? Is there a way, or is it just impossible? If so, then should I be using another approach entirely? Thanks.
A better way:
SELECT ID
FROM (
SELECT ID,
"Date",
Event,
LAST_VALUE( CASE Event WHEN 'AnotherEvent' THEN "Date" END IGNORE NULLS )
OVER ( PARTITION BY ID ORDER BY "Date"
ROWS BETWEEN UNBOUNDED PRECEEDING AND UNBOUNDED FOLLOWING
) AS another_date,
FIRST_VALUE( CASE Event WHEN 'ThirdEvent' THEN "Date" END IGNORE NULLS )
OVER ( PARTITION BY ID ORDER BY "Date"
ROWS BETWEEN UNBOUNDED PRECEEDING AND UNBOUNDED FOLLOWING
) AS third_date
FROM TableA
WHERE Event IN ( 'MainEvent', 'ThirdEvent' )
OR ( Event = 'AnotherEvent' AND EXTRACT( YEAR FROM "Date" ) = 2015 )
)
WHERE Event = 'MainEvent'
AND "Date" >= another_date
AND ( third_date IS NULL OR third_date > another_date );
You need to join your DateX CTE on the ID column. Something like:
WITH
DateX AS
(
SELECT
TableSub.ID,
MAX(TableSub.Date) AS MaxDate
FROM
TableA TableSub
WHERE
AND TableSub.Event = 'AnotherEvent'
AND TableSub.Date >= DATE '2015-01-01'
AND TableSub.Date < DATE '2016-01-01'
GROUP BY
TableSub.ID
)
SELECT
TableMain.ID
FROM
TableA TableMain
JOIN
DateX
ON
DateX.ID = TableMain.ID
WHERE
TableMain.Event = 'MainEvent'
AND TableMain.Date >= DateX.MaxDate
AND (
SELECT
TableSub2.ID
FROM
TableA TableSub2
JOIN
DateX
ON
DateX.ID = TableSub2.ID
WHERE
TableSub2.ID = TableMain.ID
TableSub2.Event = 'ThirdEvent'
AND TableSub2.Date <= DateX.MaxDate
) IS NULL
The CTE also needs a column alias for the aggregate; and as you need to join in the ID, you need to include that and group by it.
The last subquery looks odd; you might want NOT EXISTS rather than IS NULL if you're looking for no record. Perhaps your real query is using an aggregate, but even so that might be quicker.
This still may not be the best approach but it's hard to tell from your example. Hitting the same table three times may be unnecessarily expensive.

SQL issue with NULL values on SUM

I'm currently working on some sql stuff, but running in a bit of an issue.
I've got this method that looks for cash transactions, and takes off the cashback but sometimes there are no cash transactions, so that value turns into NULL and you can't subtract from NULL.
I've tried to put an ISNULL around it, but it still turns into null.
Can anyone help me with this?
;WITH tran_payment AS
(
SELECT 1 AS payment_method, NULL AS payment_amount, null as tran_header_cid
UNION ALL
SELECT 998 AS payment_method, 2 AS payment_amount, NULL as tran_header_cid
),
paytype AS
(
SELECT 1 AS mopid, 2 AS mopshort
),
tran_header AS
(
SELECT 1 AS cid
)
SELECT p.mopid AS mopid,
p.mopshort AS descript,
payment_value AS PaymentValue,
ISNULL(DeclaredValue, 0.00) AS DeclaredValue
from paytype p
LEFT OUTER JOIN (SELECT CASE
When (tp.payment_method = 1)
THEN
(ISNULL(SUM(tp.payment_amount), 0)
- (SELECT ISNULL(SUM(ABS(tp.payment_amount)), 0)
FROM tran_payment tp
INNER JOIN tran_header th on tp.tran_header_cid = th.cid
WHERE payment_method = 998
) )
ELSE SUM(tp.payment_amount)
END as payment_value,
tp.payment_method,
0 as DeclaredValue
FROM tran_header th
LEFT OUTER JOIN tran_payment tp
ON tp.tran_header_cid = th.cid
GROUP BY payment_method) pmts
ON p.mopid = pmts.payment_method
Maybe COALESCE() can help you?
You can try this:
SUM(COALESCE(tp.payment_amount, 0))
or
COALESCE(SUM(tp.payment_amount), 0)
COALESCE(arg1, arg2, ..., argN) returns the first non-null argument from the list.
try to put ISNULL inside SUM and ABS, i.e. around the actual field, like this
SUM(ISNULL(tp.payment_amount, 0))
SUM(ABS(ISNULL(tp.payment_amount, 0)))
I don't have MS SQL to test here, but would it work to put the ISNULL around the SELECT? Maybe, ISNULL isn't triggered at all, if there are no matching rows...