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
Related
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:
I created a chart that will plot data to show value for present data on specific date
with this SQL
select substring(TRAN_DATE, 1, 4) MONTH, substring(TRAN_DATE, 5, 2) DATE, count(*) AMOUNT from TA1606 group by TRAN_DATE
this is field structure
TRAN_DATE char(6)
with this create command
CREATE TABLE [dbo].[TA16](
[TRAN_DATE] [char](6) NULL,
[TERM] [char](16) NULL,
)
this my result is
MONTH DATE AMOUNT
1606 03 44
but this is just only one row and one day
i need to get all day of month in example is 06 June it will query all day in the month then mapping data to date as my result above shown
this is my expected result
MONTH DATE AMOUNT
1606 1 0
1606 2 0
1606 3 44
1606 4 0
1606 5 0
1606 6 0
1606 7 0
1606 8 0
1606 9 0
1606 10 0
1606 11 0
1606 12 0
1606 13 0
1606 14 0
1606 15 0
1606 16 0
1606 17 0
1606 18 0
1606 19 0
1606 20 0
1606 21 0
1606 22 0
1606 23 0
1606 24 0
1606 25 0
1606 26 0
1606 27 0
1606 28 0
1606 29 0
1606 30 0
Basically the idea is to use a left join between a calendar table and your data table.
There are many ways to create a calendar table, I like to use a tally table for that.
So the First thing you should do, if you don't already have a tally table, is to create one.
If you are asking your self what is a tally table and why do you need it, read this article by Jeff Moden
SELECT TOP 1001 IDENTITY(int,0,1) AS Number
INTO Tally
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE Tally ADD CONSTRAINT PK_Tally PRIMARY KEY CLUSTERED (Number)
Now, Create and populate sample data (Please save us this step in your next question)
DECLARE #TA16 TABLE (
[TRAN_DATE] [char](6) NULL,
[TERM] [char](16) NULL
)
INSERT INTO #TA16 VALUES ('160603', '44')
Then, Use a CTE to create a calendar for the relevant year (or month, what ever is best for you),
And since you are using char to keep the date, we need another cte to convert that char value to proper date.
;With Calendar as
(
SELECT DATEADD(DAY, Number, '2016-01-01') TheDate
FROM Tally
WHERE Number < 367 -- Most years are 365 days, but leap years are 366, so it will cover leap years as well
), TA16WithActualDate AS
(
SELECT [TRAN_DATE],
[TERM],
CONVERT(date, LEFT([TRAN_DATE], 2) +'.'+ SUBSTRING ([TRAN_DATE], 3, 2) +'.'+ RIGHT([TRAN_DATE], 2), 2) As ActualDate
FROM #TA16
)
Now, select from Calendar left joined to the TA16WithActualDate cte and format the output as you wish:
SELECT REPLACE(CONVERT(char(5), TheDate, 2), '.', '') As [Month],
RIGHT(CONVERT(char(8), TheDate, 2), 2) As [Date],
ISNULL([TERM], 0) As Amount
FROM Calendar
LEFT JOIN TA16WithActualDate ON TheDate = TRAN_DATE
WHERE MONTH(TheDate) = 6
Results:
Month Date Amount
--- ---- ------
1606 01 0
1606 02 0
1606 03 44
1606 04 0
1606 05 0
1606 06 0
1606 07 0
1606 08 0
1606 09 0
1606 10 0
1606 11 0
1606 12 0
1606 13 0
1606 14 0
1606 15 0
1606 16 0
1606 17 0
1606 18 0
1606 19 0
1606 20 0
1606 21 0
1606 22 0
1606 23 0
1606 24 0
1606 25 0
1606 26 0
1606 27 0
1606 28 0
1606 29 0
1606 30 0
Try like this,
SELECT TOP 1 substring(TRAN_DATE, 1, 4) MONTH
,substring(TRAN_DATE, 5, 2) DATE
,count(*) AMOUNT
FROM TA1606
GROUP BY TRAN_DATE
ORDER BY 3 DESC
This query would probably give result like this.
MONTH DATE AMOUNT
1606 3 44
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
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.
I have a table that lists visits to a clinic. I'd like to get a "histogram" of sorts showing how frequently patients visit the clinic along with totals. Here's some sample code (tested under MS SQL Server 2005) to show what I'm talking about:
CREATE TABLE #test (
visit_id int IDENTITY(1,1),
patient_id int
);
DECLARE #num_patients int;
SELECT #num_patients = 1000 + ABS(CHECKSUM(NEWID())) % 250;
INSERT INTO #test (patient_id)
SELECT TOP 15 PERCENT ABS(CHECKSUM(NEWID())) % #num_patients
FROM sysobjects a, sysobjects b;
-- SELECT COUNT(*) AS total_visits FROM #test;
-- SELECT COUNT(DISTINCT patient_id) AS distinct_patients FROM #test;
SELECT CASE GROUPING(num_pat_visits) WHEN 1 THEN 'Total'
ELSE CAST(num_pat_visits AS varchar(5)) END AS num_pat_visits,
COUNT(*) AS num_patients, num_pat_visits * COUNT(*) AS tot_pat_visit
FROM
(SELECT patient_id, COUNT(*) AS num_pat_visits FROM #test GROUP BY patient_id) a
GROUP BY num_pat_visits WITH ROLLUP
ORDER BY CAST(num_pat_visits AS int) DESC;
This gets me almost to where I want:
num_pat_visits num_patients tot_pat_visit
-------------- ------------ -------------
60 1 60
54 2 108
52 2 104
51 4 204
50 3 150
49 3 147
48 7 336
47 7 329
46 15 690
45 15 675
44 29 1276
43 36 1548
42 45 1890
41 45 1845
40 59 2360
39 71 2769
38 51 1938
37 72 2664
36 77 2772
35 74 2590
34 72 2448
33 82 2706
32 90 2880
31 74 2294
30 69 2070
29 47 1363
28 30 840
27 27 729
26 26 676
25 21 525
24 13 312
23 4 92
22 5 110
21 4 84
20 2 40
18 2 36
Total 1186 NULL
However, I can't seem to get SQL Server to display the total number of visits where it says NULL on the total row.
Any ideas?
I think you can just do:
sum(num_pat_visits) as tot_pat_visit
SELECT CASE GROUPING(num_pat_visits) WHEN 1 THEN 'Total'
ELSE CAST(num_pat_visits AS varchar(5)) END AS num_pat_visits,
COUNT(*) AS num_patients,
--num_pat_visits * COUNT(*) AS tot_pat_visit
sum(num_pat_visits) as tot_pat_visit
FROM
(SELECT patient_id, COUNT(*) AS num_pat_visits FROM #test GROUP BY patient_id) a
GROUP BY num_pat_visits WITH ROLLUP
ORDER BY CAST(num_pat_visits AS int) DESC;