T-SQL creating a hierarchy out of orderly numbers - sql

I have such table:
Id code
1 10
2 11
3 20
4 21
5 30
6 31
7 32
8 40
9 10
10 11
11 20
12 21
13 30
14 31
15 32
16 40
17 20
18 21
19 30
20 31
21 32
22 40
23 20
24 21
25 30
26 31
27 32
28 40
29 20
30 21
31 30
32 31
33 32
34 40
35 20
36 21
37 30
38 31
39 32
40 40
41 41
42 90
The column id represents simply the order of the records.
The column code represent the type of record.
The problem is that the records are part of a hierarchy, as shown here:
What I need to obtain is the parent of every record:
Id code Parent
1 10 1
2 11 1
3 20 1
4 21 3
5 30 3
6 31 3
7 32 3
8 40 3
9 10 9
10 11 9
11 20 9
12 21 11
13 30 11
14 31 11
15 32 11
16 40 11
17 20 9
18 21 17
19 30 17
20 31 17
21 32 17
22 40 17
23 20 9
24 21 23
25 30 23
26 31 23
27 32 23
28 40 23
29 20 9
30 21 29
31 30 29
32 31 29
33 32 29
34 40 29
35 20 9
36 21 35
37 30 35
38 31 35
39 32 35
40 40 35
41 41 40
42 90 42
The parent of every record should be expressed as its Id.
The rules are like this:
10s are their own parents since they are the roots
90s are their own parents since they are the end of data
20s parent is the previous 10
21 30 31 32 33 parent is the previous 20
40 and 50 parents is the previous 20
41 parent is the previous 40
As you can see the order in which records are is very important.
I tried to solve this declaratively (with lag() etc) and imperatively with loops but I could not find a solution.
Please help

This should work. Probably not optimal performance, but its pretty clear what its doing so should be easy to modify if (when!) your hierarchy changes.
It can obviously produce nulls if your hierarchy or ordering is not as you have prescribed
CREATE TABLE #data(id INT, code INT);
INSERT INTO #data values
(1 , 10),(2 , 11),(3 , 20),(4 , 21),(5 , 30),(6 , 31),(7 , 32),(8 , 40),(9 , 10),(10 , 11),
(11 , 20),(12 , 21),(13 , 30),(14 , 31),(15 , 32),(16 , 40),(17 , 20),(18 , 21),(19 , 30),(20 , 31),
(21 , 32),(22 , 40),(23 , 20),(24 , 21),(25 , 30),(26 , 31),(27 , 32),(28 , 40),(29 , 20),(30 , 21),
(31 , 30),(32 , 31),(33 , 32),(34 , 40),(35 , 20),(36 , 21),(37 , 30),(38 , 31),(39 , 32),(40 , 40),
(41 , 41),(42 , 90);
WITH
tens AS (SELECT id FROM #data WHERE code = 10),
twenties AS (SELECT id FROM #data WHERE code = 20),
forties AS (SELECT id FROM #data WHERE code = 40)
SELECT #data.id,
#data.code,
CASE WHEN code IN (10,90) THEN #data.id
WHEN code IN (11,20) THEN prev_ten.id
WHEN code IN (21,30,31,32,33,40,50) THEN prev_twenty.id
WHEN code = 41 THEN prev_forty.id
ELSE NULL
END AS Parent
FROM #data
OUTER APPLY (SELECT TOP (1) id FROM tens WHERE tens.id < #data.id ORDER BY tens.id DESC) AS prev_ten
OUTER APPLY (SELECT TOP (1) id FROM twenties WHERE twenties.id < #data.id ORDER BY twenties.id DESC) AS prev_twenty
OUTER APPLY (SELECT TOP (1) id FROM forties WHERE forties.id < #data.id ORDER BY forties.id DESC) AS prev_forty;

i think u should add FOREIGN KEY parentId referencing Id to existing table, fill this new column by UPDATE or gain data to fill it from external source and then u should do SELECT * FROM tableName ORDER BY parentId to receive tree structure

Related

yearly average from monthly daterange data

I have the following table in postgresql;
Value period
1 [2017-01-01,2017-02-01)
2 [2017-02-01,2017-03-01)
3 [2017-03-01,2017-04-01)
4 [2017-04-01,2017-05-01)
5 [2017-05-01,2017-06-01)
6 [2017-06-01,2017-07-01)
7 [2017-07-01,2017-08-01)
8 [2017-08-01,2017-09-01)
9 [2017-09-01,2017-10-01)
10 [2017-10-01,2017-11-01)
11 [2017-11-01,2017-12-01)
12 [2017-12-01,2018-01-01)
13 [2018-01-01,2018-02-01)
14 [2018-02-01,2018-03-01)
15 [2018-03-01,2018-04-01)
16 [2018-04-01,2018-05-01)
17 [2018-05-01,2018-06-01)
18 [2018-06-01,2018-07-01)
19 [2018-07-01,2018-08-01)
20 [2018-08-01,2018-09-01)
21 [2018-09-01,2018-10-01)
22 [2018-10-01,2018-11-01)
23 [2018-11-01,2018-12-01)
24 [2018-12-01,2019-01-01)
25 [2019-01-01,2019-02-01)
26 [2019-02-01,2019-03-01)
27 [2019-03-01,2019-04-01)
28 [2019-04-01,2019-05-01)
29 [2019-05-01,2019-06-01)
30 [2019-06-01,2019-07-01)
31 [2019-07-01,2019-08-01)
32 [2019-08-01,2019-09-01)
33 [2019-09-01,2019-10-01)
34 [2019-10-01,2019-11-01)
35 [2019-11-01,2019-12-01)
36 [2019-12-01,2020-01-01)
37 [2020-01-01,2020-02-01)
38 [2020-02-01,2020-03-01)
39 [2020-03-01,2020-04-01)
40 [2020-04-01,2020-05-01)
41 [2020-05-01,2020-06-01)
42 [2020-06-01,2020-07-01)
How can I get yearly average from monthly data in postgresql?
Note: Column Value is type integer and column period is type daterange.
The expected result should be
6.5 2017
18.5 2018
30.5 2019
39.5 2020
If your periods are always taking one month, including the lower bound and excluding the upper, you could try this
select
avg(value * 1.0) as average,
extract(year from lower(period)) as year
from table
group by year

Aggregate result from query by quarter SQL

Lets say I have a table which holds all exports for some time back in Microsoft SQL database:
Name:
ExportTable
Columns:
id - numeric(18)
exportdate - datetime
In order to get the number of exports per week I can run the following query:
SELECT DATEPART(ISO_WEEK,[exportdate]) as 'exportdate', count(exportdate) as 'totalExports'
FROM [ExportTable]
Group By DATEPART(ISO_WEEK,[exportdate])
order by exportdate;
Returns:
exportdate totalExports
---------- ------------
27 13
28 12
29 15
30 8
31 17
32 10
33 7
34 15
35 4
36 18
37 10
38 14
39 14
40 21
41 19
Would it be possible to aggregate the week results by quarter so the output becomes something like the bellow?
UPDATE
Sorry for not being crystal clear, I would like the current result to add upp with previous result up to a new quarter.
Note week 41 contains 21+19 = 40
Week 39 contains 157 (13+12+15+8+17+10+7+15+4+18+10+14+14)
exportdate totalExports Quarter
---------- ------------ -------
27 13 3
28 25 3
29 40 3
30 48 3
31 65 3
32 75 3
33 82 3
34 97 3
35 101 3
36 119 3
37 129 3
38 143 3
39 157 3 -- Sum of 3 Quarter values.
40 21 4 -- New Quarter show current week value
41 40 4 -- (21+19)
You can use this.
SELECT
DATEPART(ISO_WEEK,[exportdate]) as 'exportdate'
, SUM( count(exportdate) ) OVER ( PARTITION BY DATEPART(QUARTER,MIN([exportdate])) ORDER BY DATEPART(ISO_WEEK,[exportdate]) ROWS UNBOUNDED PRECEDING ) as 'totalExports'
, DATEPART(QUARTER,MIN([exportdate])) [Quarter]
FROM [ExportTable]
Group By DATEPART(ISO_WEEK,[exportdate])
order by exportdate;
You could use a case statement to separate the dates into quarters.
e.g.
CASE
WHEN EXPORT_DATE BETWEEN '1' AND '4' THEN 1
WHEN Export_Date BETWEEN '5' and '9' THEN 2
ELSE 0 AS [Quarter]
END
Its just an example but you get the idea.
You could then use the alias from the case
SELECT DATEPART(ISO_WEEK,[exportdate]) as 'exportdate', count(exportdate) as 'totalExports', DATEPART(quarter,[exportdate]) as quarter FROM [ExportTable] Group By DATEPART(ISO_WEEK,[exportdate]), DATEPART(quarter,[exportdate]) order by exportdate;

Compare Current Row with Previous/Next row in SQL Server

I have a table named team and it like below: I just added a row_number in the 3rd column
RaidNo OutComeID RN
2 15 1
4 15 2
6 14 3
8 16 4
10 16 5
12 14 6
14 16 7
16 15 8
18 15 9
20 16 10
22 12 11
24 16 12
26 16 13
28 16 14
30 15 15
32 14 16
34 13 17
When the OutcomeId came as 16 then start with one and 16 comes consecutively, add one by one. And the results be like
RaidNo OutComeID RN Result
2 15 1 0
4 15 2 0
6 14 3 0
8 16 4 1
10 16 5 2
12 14 6 0
14 16 7 1
16 15 8 0
18 15 9 0
20 16 10 1
22 12 11 0
24 16 12 1
26 16 13 2
28 16 14 3
30 15 15 0
32 14 16 0
34 13 17 0
Help me to get the result.
You can use the following query:
SELECT RaidNo, OutComeID, RN,
CASE
WHEN OutComeID <> 16 THEN 0
ELSE ROW_NUMBER() OVER (PARTITION BY OutComeID, grp ORDER BY RN)
END AS Result
FROM (
SELECT RaidNo, OutComeID, RN,
RN - ROW_NUMBER() OVER (PARTITION BY OutComeID ORDER BY RN) AS grp
FROM mytable) AS t
ORDER BY RN
Field grp identifies slices (also called islands) of consecutive records having the same OutComeID value. The outer query uses grp in order to enumerate each record that belongs to a '16' slice. The records that belong to the other slices are assigned value 0.
Demo here

How to count rows from a different table based on same date range

EDIT although the accepted answer doesn't match what I was looking for at the moment I wrote this question, it does show a better way of what I had already (which is what they want) the data in the production tables are what is wrong, meaning that the numbers are wrong.
I'm struggling to create the sql statment to create the report at the bottom of this post. I've included the statments to create my tables and test data. So to get started here is the Creation statement for the picture_stats
CREATE TABLE [picture_stats](
[PICTURE_STATS_ID] [int] NULL,
[USER_NAME] [varchar](30) NULL,
[DATE_TIME] [datetime] NULL,
[SIZE] [float] NULL,
[CLICK_COUNT] [int] NULL
) ON [PRIMARY]
GO
Insert statement that puts data into picture_stats
INSERT INTO [picture_stats]
([PICTURE_STATS_ID]
,[USER_NAME]
,[DATE_TIME]
,[SIZE]
,[CLICK_COUNT])
VALUES
(1 ,'A','2015-05-18'75,18),
(2 ,'A','2015-05-18'13,18),
(3 ,'A','2015-05-18'42,16),
(4 ,'A','2015-05-18'59,16),
(5 ,'A','2015-05-18'46,14),
(6 ,'A','2015-05-18'64,16),
(7 ,'A','2015-05-18'87,13),
(8 ,'A','2015-05-18'84,14),
(9 ,'A','2015-05-18'33,16),
(10,'A','2015-05-18'59,14),
(11,'B','2015-05-19'10,17),
(12,'B','2015-05-19'44,18),
(13,'B','2015-05-19'29,14),
(14,'B','2015-05-19'65,19),
(15,'B','2015-05-19'10,15),
(16,'B','2015-05-19'55,18),
(17,'B','2015-05-19'81,11),
(18,'B','2015-05-19'29,11),
(19,'B','2015-05-19'58,19),
(20,'B','2015-05-19'20,17),
(21,'C','2015-05-20'35,16),
(22,'C','2015-05-20'70,18),
(23,'C','2015-05-20'30,13),
(24,'C','2015-05-20'33,13),
(25,'C','2015-05-20'43,19),
(26,'C','2015-05-20'10,15),
(27,'C','2015-05-20'33,13),
(28,'C','2015-05-20'23,12),
(29,'C','2015-05-20'35,18),
(30,'C','2015-05-20'58,19)
GO
Table view of the data.
ID USER_NAME DATE_TIME SIZE CLICK_COUNT
-- --------- ---------- ---- -----------
1 A 2015-05-18 75 18
2 A 2015-05-18 1 18
3 A 2015-05-18 42 16
4 A 2015-05-18 59 16
5 A 2015-05-18 46 14
6 A 2015-05-18 64 16
7 A 2015-05-18 87 13
8 A 2015-05-18 84 14
9 A 2015-05-18 33 16
10 A 2015-05-18 59 14
11 B 2015-05-19 10 17
12 B 2015-05-19 44 18
13 B 2015-05-19 29 14
14 B 2015-05-19 65 19
15 B 2015-05-19 100 15
16 B 2015-05-19 55 18
17 B 2015-05-19 81 11
18 B 2015-05-19 29 11
19 B 2015-05-19 58 19
20 B 2015-05-19 20 17
21 C 2015-05-20 35 16
22 C 2015-05-20 7 18
23 C 2015-05-20 30 13
24 C 2015-05-20 33 13
25 C 2015-05-20 4 19
26 C 2015-05-20 100 15
27 C 2015-05-20 33 13
28 C 2015-05-20 23 12
29 C 2015-05-20 35 18
30 C 2015-05-20 58 19
The second table picture_comment_stats can be created with this:
CREATE TABLE [picture_comment_stats](
[PICTURE_STATS_ID] [int] NULL,
[USER_NAME] [varchar](30) NULL,
[DATE_TIME] [datetime] NULL,
[CLICK_COUNT] [int] NULL,
[LIKES] [int] NULL,
) ON [PRIMARY]
GO
To script in the data:
INSERT INTO [picture_comment_stats]
([PICTURE_STATS_ID]
,[USER_NAME]
,[DATE_TIME]
,[CLICK_COUNT]
,[LIKES])
VALUES
(1 ,'X','2015-05-18',75,18),
(2 ,'X','2015-05-18',1 ,18),
(3 ,'X','2015-05-18',42,16),
(4 ,'X','2015-05-18',59,16),
(9 ,'X','2015-05-19',34,16),
(10,'X','2015-05-19',57,14),
(11,'Y','2015-05-19',11,17),
(12,'Y','2015-05-19',44,18),
(17,'Y','2015-05-20',81,11),
(18,'Y','2015-05-20',29,11),
(19,'Y','2015-05-20',55,19),
(21,'Z','2015-05-20',45,16),
(20,'Y','2015-05-21',20,17),
(22,'Z','2015-05-21',7 ,18),
(23,'Z','2015-05-21',30,13),
(24,'Z','2015-05-21',39,13),
(25,'Z','2015-05-21',4 ,19),
(26,'Z','2015-05-21',10,15),
(27,'Z','2015-05-21',33,13),
(28,'Z','2015-05-21',23,12),
(29,'Z','2015-05-21',35,18),
(30,'Z','2015-05-21',58,19)
GO
The table should look like this.
ID USER_NAME DATE_TIME COMMENT_ID LIKES
-- --------- ---------- ---------- -----
1 X 2015-05-18 75 18
2 X 2015-05-18 1 18
3 X 2015-05-18 42 16
4 X 2015-05-18 59 16
9 X 2015-05-19 34 16
10 X 2015-05-19 57 14
11 Y 2015-05-19 11 17
12 Y 2015-05-19 44 18
17 Y 2015-05-20 81 11
18 Y 2015-05-20 29 11
19 Y 2015-05-20 55 19
21 Z 2015-05-20 45 16
20 Y 2015-05-21 20 17
22 Z 2015-05-21 7 18
23 Z 2015-05-21 30 13
24 Z 2015-05-21 39 13
25 Z 2015-05-21 4 19
26 Z 2015-05-21 10 15
27 Z 2015-05-21 33 13
28 Z 2015-05-21 23 12
29 Z 2015-05-21 35 18
30 Z 2015-05-21 58 19
What I want to do is make a report of the data such that I have the user name, the count of the pictures, and the count of comments on an individuals picture. The picture_stats_id in both tables is actually a foreign key which points to the table 'pictures' primary key picture_id. The report I want to be able to choose a date or a date range and have it show like so: I'm hoping the report would show the following for the different 3 day scenarios
(Filtered for Date:2015-05-18)
USER_NAME PICS COMMENTS
--------- ---- --------
A 10 4
(Filtered for Date:2015-05-19)
USER_NAME PICS COMMENTS
--------- ---- --------
B 10 2
(Filtered for Date:2015-05-20)
USER_NAME PICS COMMENTS
--------- ---- --------
C 10 1
(Filtered for Date:2015-05-18 to 2015-05-19)
USER_NAME PICS COMMENTS
--------- ---- --------
A 10 6
B 10 2
(Filtered for Date:2015-05-19 to 2015-05-20)
USER_NAME PICS COMMENTS
--------- ---- --------
B 10 2
C 10 1
(Filtered for Date:2015-05-18 to 2015-05-20)
USER_NAME PICS COMMENTS
--------- ---- --------
A 10 6
B 10 5
C 10 1
so far all I have is
SELECT ps.USER_NAME as USER_NAME,
COUNT(*) as PICTURE_COUNT,
SUM(1) AS COMMENT_COUNT -- can't figure out this statement for nothing.
from picture_comment_stats pcs
RIGHT OUTER JOIN picture_stats ps ON ps.PICTURE_STATS_ID = pcs.PICTURE_STATS_ID
--WHERE ps.DATE_TIME BETWEEN #beginFilterDate AND #endFilterDate
GROUP BY ps.USER_NAME
ORDER BY ps.USER_NAME
EDIT
The fiddle address as was given in the below comments: http://sqlfiddle.com/#!3/74e77
To clarify the counts I'm try to get. The second column, would be the number of pictures said user posted that day. the third column is how many comments were made on a users picture for that specified day ONLY. So even though X commented on A's picture a total of 6 times (on the 18th, and 19th) I only want to count 4 if I filter for the 18th, and 6 if i filter on the 18th-19th. It's a little hard to follow I'm sure, but that's what is wanted.
SELECT --ps.USER_NAME as USER_NAME,
s.user_name,
count(distinct c.comment_id) as comments,
count(s.picture_stats_id) as pics
--COUNT(*) as PICTURE_COUNT,
--SUM(1) AS COMMENT_COUNT
from --commenting.transcription_audit_stats pcs
-- RIGHT OUTER JOIN commenting.transcription_stats ps ON ps.SURVEY_STATS_ID --= pcs.SURVEY_STATS_ID
picture_stats s
left join picture_comment_stats c
on s.picture_stats_id = c.picture_stats_id
and s.date_time = c.date_time
where s.date_time between --specified date range
--WHERE ps.DATE_TIME BETWEEN #beginFilterDate AND #endFilterDate
--GROUP BY ps.USER_NAME
--ORDER BY ps.USER_NAME
group by s.user_name,s.date_time
Try this and get the user name from the lookup table you have. Also filter on the dates you need.

Select sum of condition

I have a table like below. I want to select some rows that sum of cnt<120. how to do this?
cnt id
_________
6 14001
17 14005
14 14017
16 14024
9 14025
7 14027
10 14029
14 14048
23 14055
18 14056
19 14058
18 14059
18 14063
15 14064
9 14086
17 14095
9 14098
14 14116
10 14138
8 14147
17 14165
22 14171
22 14191
18 14194
13 14204
17 14221
13 14245
14 14249
6 14254
17 14257
9 14260
19 14261
26 14263
6 14264
27 14265
19 14269
11 14287
SELECT SUM(cnt)
FROM my_table
WHERE cnt < 120
You can try this:-
SELECT *
FROM test O
HAVING (SELECT sum(cnt) FROM test WHERE cnt <= O.cnt) <120