Insert into second row of table - sql

I want to insert values to second row of table. First row of table has value and it’s constant. The “total” in each row is ((Total from previous row + Entry)-Exit).
But it doesn’t calculate first row(Constant) in total.
My code is:
INSERT INTO MyTable([MDate], [MEntry], [MExit], [MTotal])
SELECT
EDate, SUM(count1) EntryC, SUM(count2) ExitC,
SUM(SUM(count1) - SUM(count2)) OVER (ORDER BY EDate) Total1
FROM
(SELECT
SUBSTRING(EntryDate, 1, 4) EDate,
COUNT(*) count1, 0 count2, 0t
FROM
[dbo].[Dash]
WHERE
SUBSTRING(EntryDate, 1, 4) > 2010
GROUP BY
SUBSTRING(EntryDate, 1, 4)
UNION
SELECT
SUBSTRING(CntEndDate, 1, 4) EDate, 0 count1,
COUNT(*) count2, 0t
FROM
[dbo].[Dash]
WHERE
PostNo <> 0 AND EmpTypeNo = 7
GROUP BY
SUBSTRING(CntEndDate, 1, 4)) A
GROUP BY
EDate
My output is:
MDate MEntry MExit Total
----------------------------
2010 110 107 549
2011 80 41 850
Total=850 is wrong, it should be 588.

Based on hour description, the calculation for the total should look like:
select mdate, mentry, mexit,
(max(case when seqnum = 1 then total else 0 end) over (order by mdate) +
sum(case when seqnum > 1 then mentry - mexit else 0 end) over (order by mdate)
) as new_total
from (select d.*, row_number() over (order by mdate) as seqnum
from dash d
) d
I'm not quite sure how that fits in with the rest oft he question, though.

Related

Indicate a row that cause an abnormal case (SQL)

I have a result as below using the following script:
SELECT
id, (2022 - age) yearId, age, [value],
CASE
WHEN LAG([value], 1, 0) OVER (PARTITION BY id ORDER BY [age]) = 0
THEN 'Base'
WHEN [value] > LAG([value], 1, -1) OVER (PARTITION BY id ORDER BY [age])
THEN 'Increasing'
WHEN [value] = LAG([value], 1, -1) OVER (PARTITION BY id ORDER BY [age])
THEN 'No Change'
ELSE 'Decreasing'
END AS [Order]
FROM Test
Values
And I manage to get a group of ids with an id causing a "flip: decreasing and then increasing or the other way around" as:
Abnormal Case
Now I want to print out the same result as above but with a column indicates the row that cause the flip, something like this (the row causes the flip should be place at the top of each partition):
Id
age
value
flip
1
4
3
1
1
0
5
0
1
1
4
0
1
2
3
0
1
3
2
0
1
5
3
0
1
6
4
0
Thank you!
Expanding your existing logic to get the previous order value then conditionally ordering
with cte as
(
SELECT
id, (2022 - age) yearId, age, [value],
CASE
WHEN LAG([value], 1, 0) OVER (PARTITION BY id ORDER BY [age]) = 0
THEN 'Base'
WHEN [value] > LAG([value], 1, -1) OVER (PARTITION BY id ORDER BY [age])
THEN 'Increasing'
WHEN [value] = LAG([value], 1, -1) OVER (PARTITION BY id ORDER BY [age])
THEN 'No Change'
ELSE 'Decreasing'
END AS [Order]
FROM T1
) ,
cte1 as
(select cte.*,concat(cte.[order], lag([order]) over (partition by id order by age)) concatlag
from cte)
select * ,
case when concatlag in('IncreasingDecreasing','DecreasingIncreasing') then 1 else 0 end
from cte1
order by
case when concatlag in('IncreasingDecreasing','DecreasingIncreasing') then 1 else 0 end desc,
age

Counting number of orders depending on city

I have a temp table that is being created, we will say that column 1 is an order_id, and column 2 is user_id, column 3 is start_date, column 4 is end_date and column 5 is city.
order_id user_id Start_date end_date city
101 1 200001 200101 X
101 2 200101 200110 y
101 3 200110 200112 z
101 3 200112 200210 z
I want to count by city the number of order_ids that moved out of it to another city and in another column the number of order_ids that moved into it from another city.
I would like it to come out as a table, like this:
city moved_out_orders moved_into_orders
x 1 0
y 1 1
z 0 1
You can do:
with
x as (
select a.city as from_city, b.city as to_city
from t a
join t b on a.order_id = b.order_id
and a.city <> b.city
and a.end_date = b.start_date
),
o (city, cnt) as (
select from_city, count(*) from x group by from_city
),
i (city, cnt) as (
select to_city, count(*) from x group by to_city
)
select
coalesce(i.city, o.city) as city,
o.cnt as moved_out_orders,
i.cnt as moved_in_orders
from i
full join o on o.city = i.city
Hmmm . . . I think you just want to enumerate the rows for each order and then discard the highest and lowest for each count:
select city,
sum(case when seqnum_desc > 1 then 1 else 0 end) as moved_out,
sum(case when seqnum_asc > 1 then 1 else 0 end) as moved_in
from (select t.*,
row_number() over (partition by orderid order by startdate) as seqnum_asc,
row_number() over (partition by orderid order by startdate desc) as seqnum_desc
from t
) t
group by city;
EDIT:
You appear to have adjacent rows in the same city. Seems strange, but instead you can use lead() and lag():
select city,
sum(case when next_city <> city then 1 else 0 end) as moved_out,
sum(case when prev_city <> city then 1 else 0 end) as moved_in
from (select t.*,
lag(city) over (partition by orderid order by startdate) as prev_city,
lead(city) over (partition by orderid order by startdate) as next_city
from t
) t
group by city;

Assign column value based on the percentage of rows

In DB2 is there a way to assign a column value based on the first x%, then y% and remaining z% of rows?
I've tried using row_number() function but no luck!
Example below
Assuming that the below example count(id) is already arranged in descending order
Input:
ID count(id)
5 10
3 8
1 5
4 3
2 1
Output:
First 30% rows of the above input should be assigned code H, last 30% of the rows will have code L and remaining will have code M. If 30% of rows evaluates to decimal then round up-to 0 decimal place.
ID code
5 H
3 H
1 M
4 L
2 L
You can use window functions:
select t.id,
(case ntile(3) over (order by count(id) desc)
when 1 then 'H'
when 2 then 'M'
when 3 then 'L'
end) as grp
from t
group by t.id;
This puts them into equal sized groups.
For 30-40-30% split with your conditions, you have to be more careful:
select t.id,
(case when (seqnum - 1.0) < 0.3 * cnt then 'H'
when (seqnum + 1.0) > 0.7 * cnt then 'L'
else 'M'
end) as grp
from (select t.id,
count(*) as cnt,
count(*) over () as num_ids,
row_number() over (order by count(*) desc) as seqnum
from t
group by t.id
) t
Try this:
with t(ID, count_id) as (values
(5, 10)
, (3, 8)
, (1, 5)
, (4, 3)
, (2, 1)
)
select t.*
, case
when pst <=30 then 'H'
when pst <=70 then 'M'
else 'L'
end as code
from
(
select t.*
, rownumber() over (order by count_id desc) as rn
, 100*rownumber() over (order by count_id desc)/nullif(count(1) over(), 0) as pst
from t
) t;
The result is:
ID COUNT_ID RN PST CODE
-- -------- -- --- ----
5 10 1 20 H
3 8 2 40 M
1 5 3 60 M
4 3 4 80 L
2 1 5 100 L

SQL: Check if n consecutive records are greater than some value

I have a table that contains numbers. I have to find whether there is any case where in n consecutive numbers are greater than some threshold value m.
For e.g.
id delta
---------------
1 10
4 15
11 22
23 23
46 21
57 9
So here, if I want to know if there are 3 consecutive records where value is more than 20 then I should get True. And False when I check for 4 consecutive records. Is that possible? This is on Apache Spark SQL. Thanks.
You can do this using lag:
select t.*
from (select t.*,
lag(val, 1) over (order by id) as val_1,
lag(val, 2) over (order by id) as val_2
from t
) t
where val > 20 and val_1 > 20 and val_2 > 20;
This returns the first row that is part of each three-some. If you just want true/false:
select (case when count(*) > 0 then 'true' else 'false' end)
from (select t.*,
lag(val, 1) over (order by id) as val_1,
lag(val, 2) over (order by id) as val_2
from t
) t
where val > 20 and val_1 > 20 and val_2 > 20;
EDIT:
I missed the part about not wanting more than 3. So, you can enhance this:
select (case when count(*) > 0 then 'true' else 'false' end)
from (select t.*,
lag(val, 1) over (order by id) as val_1,
lag(val, 2) over (order by id) as val_2,
lag(val, 3) over (order by id) as val_3,
lead(val, 1) over (order by id) as val_next_1
from t
) t
where (val_3 <= 20 or val_3 is null) and
(val_2 > 20 and val_1 > 20 and val > 20) and
(val_next_1 <= 20 or val_next_1 is null);
It is a little tricky because the values can be at the beginning or end of the rows.

SQL: How to group data into bands

I've created a query that shows the number of times an individual client appears in a list of transactions....
select Client_Ref, count(*)
from Transactions
where Start_Date >= '2015-01-01'
group by Client_Ref
order by Client_Ref
...this returns data like this...
Client1 1
Client2 4
Client3 1
Client4 3
..What I need to do is summarize this into bands of frequency so that I get something like this...
No. of Clients with 1 transaction 53
No. of Clients with 2 transaction 157
No. of Clients with 3 transaction 25
No. of Clients with >3 transactions 259
I can't think how to so this in SQL, I could probably figure it out in Excel but I'd rather it was done at server level.
I call this a "histogram of histogram" query. Just use group by twice:
select cnt, count(*), min(CLlient_Ref), max(Client_Ref)
from (select Client_Ref, count(*) as cnt
from Transactions
where Start_Date >= '2015-01-01'
group by Client_Ref
) t
group by cnt
order by cnt;
I include the min and max client ref, because I often want to investigate certain values further.
If you want a limit at 3, you can use case:
select (case when cnt <= 3 then cast(cnt as varchar(255)) else '4+' end) as grp,
count(*), min(CLlient_Ref), max(Client_Ref)
from (select Client_Ref, count(*) as cnt
from Transactions
where Start_Date >= '2015-01-01'
group by Client_Ref
) t
group by (case when cnt <= 3 then cast(cnt as varchar(255)) else '4+' end)
order by min(cnt);
select cnt, count(*) from
(
select case count(*) when 1 then 'No. of Clients with 1 transaction'
when 2 then 'No. of Clients with 2 transactions'
when 3 then 'No. of Clients with 3 transactions'
else 'No. of Clients with >3 transactions'
end as cnt
from Transactions
where Start_Date >= '2015-01-01'
group by Client_Ref
)
group by cnt
You can do a conditional SUM() to pull the total for each grouping:
Select 'No. of Clients with 1 transaction' = Sum(Case When A.Total = 1 Then 1 Else 0 End),
'No. of Clients with 2 transactions' = Sum(Case When A.Total = 2 Then 1 Else 0 End),
'No. of Clients with 3 transactions' = Sum(Case When A.Total = 3 Then 1 Else 0 End),
'No. of Clients with >3 transactions' = Sum(Case When A.Total > 3 Then 1 Else 0 End)
From
(
Select Client_Ref, count(*) As Total
From Transactions
Where Start_Date >= '2015-01-01'
Group by Client_Ref
) A
You can create the buckets separately and then use a union all:
with COUNT1 as (
select Client_Ref, count(*) as count1
from Transactions
where Start_Date >= '2015-01-01'
group by Client_Ref
order by Client_Ref
)
,COUNT2 as (
select cast(C.count1 as varchar(5)) as count1,count(Client_Ref) as count2
from COUNT1 C
where count1 <= 3
group by C.count1
)
,COUNT3 as (
select '> 3' as count1,count(*) as count2
from COUNT1 C
where C.count1 > 3
)
select * from COUNT2
union all
select * from COUNT3
You can manually enter that text ('No. of Clients with N transactions') if you want to.
select Client_Ref
,count(*) as Count
,case when count(*) < 4 then count(*) else 4 end as Band
from Transactions
where Start_Date >= '2015-01-01'
group by Client_Ref
order by Client_Ref