Filling null row values with last non-null values - SQL table - sql-server

I'm struggling with the following problem. Consider the example table posted below. What I need to do is to update the table, specifically the NULL values on each row with the "last" non-NULL values. For example, the NULL values on rows 3 and 4 should be updated with the values of row 2 of the same column, that is
2 007585102 2001 03 31 2001 04 12 2 154980 6300 154980 6300
3 007585102 2001 03 31 2001 04 19 2 154980 6300 154980 6300
4 007585102 2001 03 31 2001 04 26 2 154980 6300 154980 6300
and NULL values on rows 9 to 15 updated with the values of row 8 and so on.
I honestly have no idea how to do this and I will greatly appreciate any help. Thanks in advance.
Sorry about the extremely poor formatting of the table but I can't post anything but plain text.
EXAMPLE TABLE
1 007585102 2001 03 31 2001 04 05 2 543660 22100 543660 22100
2 007585102 2001 03 31 2001 04 12 2 154980 6300 154980 6300
3 007585102 NULL 2001 04 19 NULL NULL NULL NULL NULL
4 007585102 NULL 2001 04 26 NULL NULL NULL NULL NULL
5 007585102 2001 03 31 2001 05 03 2 2726664 110840 2726664 110840
6 007585102 2001 03 31 2001 05 10 2 836400 34000 836400 34000
7 007585102 2001 03 31 2001 05 17 2 534804 21740 7634364 310340
8 007585102 2001 03 31 2001 05 24 2 4920 200 4920 200
9 007585102 NULL 2001 05 31 NULL NULL NULL NULL NULL
10 007585102 NULL 2001 06 07 NULL NULL NULL NULL NULL
11 007585102 NULL 2001 06 14 NULL NULL NULL NULL NULL
12 007585102 NULL 2001 06 21 NULL NULL NULL NULL NULL
13 007585102 NULL 2001 06 28 NULL NULL NULL NULL NULL
14 007585102 NULL 2001 07 05 NULL NULL NULL NULL NULL
15 007585102 NULL 2001 07 12 NULL NULL NULL NULL NULL
16 007585102 2001 06 30 2001 07 19 2 2693301 118300 2693301 118300
17 007585102 2001 06 30 2001 07 26 2 232220 10200 NULL NULL

I'm not very proud of my answer, but at least it works. Find more elegant way on your own. I'd suggest recursive cte.
drop table #temp
GO
select
*
into #temp
from (
select 1 as id, '2001 03 31' as dat union all
select 2, '2001 03 31' union all
select 3, null union all
select 4, null union all
select 5, '2001 03 31' union all
select 6, '2001 03 31' union all
select 7, '2001 03 31' union all
select 8, '2001 03 31' union all
select 9, null union all
select 10, null union all
select 11, null union all
select 12, null union all
select 13, null union all
select 14, null union all
select 15, null union all
select 16, '2001 06 30' union all
select 17, '2001 06 30'
) x
update t
set
t.dat = t2.dat
from #temp t
join (
select
t1.id, max(t2.id) as maxid
from #temp t1
join #temp t2
on t1.id>t2.id
and t2.dat is not null
and t1.dat is null
group by
t1.id
) x
on t.id=x.id
join #temp t2
on t2.id=x.maxid
select * from #temp

I have explained this in details here:
https://koukia.ca/common-sql-problems-filling-null-values-with-preceding-non-null-values-ad538c9e62a6#.k0dxirgwu
here is the TSQL you need,
SELECT *
INTO #Temp
FROM ImportedSales;
;With CTE
As
(
SELECT ProductName
, Id
, COUNT(ProductName) OVER(ORDER BY Id ROWS UNBOUNDED PRECEDING) As MyGroup
FROM #Temp
),
GetProduct
AS
(
SELECT [ProductName]
, First_Value(ProductName) OVER(PARTITION BY MyGroup ORDER BY Id ROWS UNBOUNDED PRECEDING) As UpdatedProductName
FROM CTE
)
UPDATE GetProduct
Set ProductName = UpdatedProductName;
SELECT *
FROM #TemP;

In redshift, and I think other sql flavors, you can combine nvl() and lag functions, being sure to use the ignore nulls option when using lag().
I adapted this from https://blog.jooq.org/2015/12/17/how-to-fill-sparse-data-with-the-previous-non-empty-value-in-sql/ .
I'm going to the call the second date field in your example "date_field" since I didn't see a column header.
Let's assume you have a column called "row_number" on which you can correctly order your values.
Then the example using your above data would be like
select nvl(date_field,lag(date_field,1)) ignore nulls over ([partition by whatever] order by rownumber).
This should grab the nearest non-null value above in the column (which is ordered by whatever columns you specified), and replace the nulls until it hits a non-null value.
Ignore nulls is the key b/c otherwise you'll just grab the first non-null, then the next null, so that you only replace one row.
HTH.

Related

Grouping items and setting a flag

I have a table structured as follows:
order_yr acct_id indv_id age
2019 323 01 38
2019 323 02 37
2019 323 03 16
2019 323 04 5
2019 325 01 38
2019 326 01 64
2019 326 02 63
What I need to do is by order_yr and acct_id add a flag if the order_yr and acct_id has someone age <=17.
The result would be like this:
order_yr acct_id indv_id age child_flg
2019 323 01 38 1
2019 323 02 37 1
2019 323 03 16 1
2019 323 04 5 1
2019 325 01 38 0
2019 326 01 64 0
2019 326 02 63 0
I know I have to partition by order_yr and acct_id, but not sure how to get the result in one inline script.
Any help would be appreciated.
BTW this is an individual level extract with a number of other columns associated with each indv.
I've not gotten very far - I have this:
,ROW_NUMBER() OVER(PARTITION BY order_yr, acct_id ORDER BY (CASE WHEN age <=17 THEN 'Y' ELSE 'N' END) desc) AS CHILD_flg
You have some options here. One is using a subquery to find out if a row exists that belongs to a group and meets your condition:
select *
, case
when exists (select *
from #data sub
where sub.order_yr = d.order_yr
and sub.acct_id = d.acct_id
and sub.age <= 17)
then 1
else 0
end as flag
from #data d
You can also go with a window function like you planned:
select *
, max(case when age <= 17 then 1 else 0 end) over (partition by order_yr, acct_id) as flag
from #data d
Working demo on dbfiddle

SQL - Select non repeating columns

I have a table like
id name R_id mgr_id
----------------------------
61 a 22 1
62 a 22 2
62 b 23 1
63 c 24 4
63 b 22 3
64 c 25 3
and I would like to get the following result set
R_id mgr_id
--------------
22 1
23 1
24 4
25 3
I would like select repeating R_ids only once
I tried using this query but with not much success, can anyone help me.
SELECT DISTINCT R_id, mgr_id from DT
Perhaps something like this... WITH TIES clause in concert with Row_NUmber()
Example
Select Top 1 with ties
R_ID
,mgr_id
From #YourTable
Order By Row_Number() over (Partition By R_ID order by Mgr_id)
Returns
R_ID mgr_id
22 1
23 1
24 4
25 3

Custom aggregations in SQL

I have a table named industry. There are 6 fields. The schema is given below.
In this case, am needing to perform custom aggregations. There are 22 areas in the database. Two custom aggregations need to be made:
Areas 1-17 need to be combined into a new area with value 00.
Areas 20 and 21 need to be made into another with code value 99.
Next is my attempt at an overall framework for this. I am assuming that creating a new table is the simplest way to accopmlish this. At the bottom is a very short example of the intended result.
create table industry2
(
year char(4),
qtr char(2),
area char(6),
industry char(3),
ownership char(2),
employment numeric(8,0)
);
INSERT INTO Industry2
(year, qtr, area, industry, ownership, employment)
SELECT year, qtr, area, (select sum (employment) from dbo.industry where area
= '01' or area = '02' and so on):
2017 01 01 123000 1 456
2017 01 02 123000 1 101
2017 01 03 123000 1 103
2017 01 01 134000 1 6
2017 01 02 134000 1 7
2017 01 03 134000 1 12
2017 01 09 134000 1 1
2017 01 01 144000 1 14
2017 01 20 134000 1 7
2017 01 21 134000 1 8
Intended result
2017 01 00 123000 1 660
2017 01 00 134000 1 26
2017 01 00 144000 1 14
2017 01 99 134000 1 15
You can define your custom GROUP BY clause with a CASE WHEN statement:
select [year],
[qtr],
case when [area] in('20','21') then '99' when [area] between 1 and 17 then '00' end as [area],
[industry],
[ownership],
sum([employment]) as [employment_sum]
from industry2
group by
[year],
[qtr],
case when [area] in('20','21') then '99' when [area] between 1 and 17 then '00' end,
[industry],
[ownership]
Result:

Conditional calculation in SQL Server

friends:
I'm now have a problem about conditional calculation in SQL Server.
I have set some data from SQL Server as an example in excel like this:
No Employee Month Commission1 Commission2
1 A Jan 10 5
2 A Jan 10 4
3 B Jan 15 3
4 B Jan 15 4
5 C Jan 10 3
6 C Jan 10 4
7 D Jan 13 3
8 D Jan 13 4
9 DM Jan 0 6
10 DM Jan 0 8
11 A Feb 15 4
12 A Feb 15 5
13 B Feb 20 5
14 B Feb 20 4
15 C Feb 9 3
16 C Feb 9 4
17 D Feb 14 5
18 D Feb 14 6
19 DM Feb 0 13
20 DM Feb 0 10
And the result I want is like this:
Employee Jan No# Feb No#
A 20 2 30 2
B 30 2 40 2
C 20 2 18 2
D 26 2 28 2
DM 44 10 59 10
For every sales,Employee A,B,C,D only have commission1 as payment,the commission2 is for DM. So , in Jan , DM's commission is SUM(E2:E9)
I can do it easy in excel , but how can I do this in sql server?
I make my try code like this:
select [Month],Employee,SUM(Commission1) Commission,count(distinct([No])) No#
from table1
WHERE Employee IN ('A','B','C','D')
group by [Month],Employee
union
select 'DM' as Employee,[Month],SUM(Commission2) Commission,count(distinct([No])) No#
from table1
WHERE Employee IN ('A','B','C','D','DM')
group by [Month],Employee
And I get the result
Employee Month Commission No#
A Jan 20 2
B Jan 30 2
C Jan 20 2
D Jan 26 2
DM Jan 44 10
A Feb 30 2
B Feb 40 2
C Feb 18 2
D Feb 28 2
DM Feb 59 10
The result format is not what I want.I tried pivot after this query,but failed,it seems I only can pivot one state?
Another question: If I want the month growth automatic (In actual data , there's not only Jan and Feb) in the result ,not write [Jan],[Feb],[Mar]... in pivot code, how to do it?
Who can help me?
Thanks!
Here is a PIVOT solution:
Test data:
DECLARE #t table(Employee varchar(2), Month char(3), Commission1 int, Commission2 int)
INSERT #t values
('A','Jan',10,5 ),('A','Jan',10,4),('B','Jan',15,3),
('B','Jan',15,4 ),('C','Jan',10,3),('C','Jan',10,4),
('D','Jan',13,3 ),('D','Jan',13,4),('DM','Jan',0,6),
('DM','Jan',0,8 ),('A','Feb',15,4),('A','Feb',15,5),
('B','Feb',20,5 ),('B','Feb',20,4),('C','Feb',9,3),
('C','Feb',9,4 ),('D','Feb',14,5),('D','Feb',14,6),
('DM','Feb',0,13),('DM','Feb',0,10)
Query:
;WITH CTE as
(
SELECT
Employee, Month,
CASE WHEN Employee = 'DM' THEN
SUM(Commission2) over (partition by [Month])
ELSE Commission1 END com,
CASE WHEN Employee = 'DM'
THEN row_number() over
(PARTITION BY Employee, [Month] ORDER BY (SELECT 1)) ELSE 1 END rn
FROM #t
)
SELECT Employee, [Jan], [Feb], [Mar] -- add more months
FROM
CTE
PIVOT
(SUM(com)
FOR [Month] IN ([Jan], [Feb], [Mar])) AS pvt -- add more months
WHERE rn = 1
Result:
Employee Jan Feb Mar
A 20 30 NULL
B 30 40 NULL
C 20 18 NULL
D 26 28 NULL
DM 44 59 NULL
In SqlServer you can do so using PIVOT operator as like below:
Please refer PIVOT syntax
select tmp.employee,pv.[jan] as Jan_Commission, pv.[feb] as Feb_Commission from
(
select employee,month,commission1 from table_name
)tmp
pivot (
sum(commission1)
for [month] in ([jan],[feb])
)pv;

Query to compare two year sales

I have a table in SQL Server with these columns:
Year
Month
Product
Qty
Example:
Year Month Product Qty
2011 1 XYZQW 45
So in this table was stored all product sales.
I need to build a query to compare one year and its previous to build this report:
Year GEN FEB MAR APR MAY GIU JUL AUG SEP OCT NOV DEC
-------------------------------------------------------
2011 12 23 56 54 14 11 15 18 89 87 48 98
2012 19 21 55 50 24 10 19 17 88 81 45 90
There is a way to do this without creating a temporary table?
This is simple, try this:
WITH DT(AMonth,AYear,AQty)
AS (SELECT Month, Year, Qty
FROM YourTable)
SELECT pvt.*
FROM DT cte
PIVOT
(SUM(AQty)
FOR AYear IN ( [2011],[2012],[2013],[2014] ) ) AS pvt

Resources