PostgreSQL calculate difference between multi rows - sql

I tried to calculate difference between rows in a field using a query:
Illustrations:
input:year,month,name, size
output:increase
year | month | name | Size | increase
------+--------+------- -+-------+-----------
2020 | 01 | john |10 | 0
2020 | 01 | peter |12 | 0
2020 | 01 | kim |16 | 0
2020 | 02 | john |15 | 5 <- 15 - 10
2020 | 02 | peter |16 | 4 <- 16 - 12
2020 | 02 | kim |17 | 1 <- 17 - 16
2020 | 03 | john |18 | 3 <- 18 - 16
2020 | 03 | peter |19 | 3 <- 19 - 16
2020 | 03 | kim |77 | 60 <- 77 - 17
-------
2020 | 12 | john |25 | 17
2020 | 12 | peter |70 | 33
2020 | 12 | kim |90 |42
Increase column as output by difference between adjacent "name" rows in size.

Use LAG()
select year,
month,
name,
size,
size - lag(size) over (partition by name order by year, month) as increase
from MyTable

If you want 0s for the first set of rows, then use the 3-argument form of lag():
select year, month, name, size,
(size - lag(size, 1, size)) over (partition by name order by year, month) as increase
from MyTable;
Personally, I prefer NULL so I prefer JohnHC's answer. However, the question is explicitly asking for 0 values there.

Thanks JohnHC and Gorden for helping.
It works when I run the query on psql commandline. But when I put it into a php script:
$result = pg_query($conn,"select year, month, name, (size - lag(size, 1, size)) over (partition by name order by year, month). as increase from testdb ");
I get error message:
PHP Warning: pg_query(): Query failed: ERROR: syntax error at or near '- lag(size)......'

Related

MDX running total with cross join

MDX newbie question
I am using the FoodMart example database to express my problem.
I need to show running total of the "Unit Sales" measure on the column
with "Month", "Year", and "Product Name" dimensions on the rows:
Year Month Product | Unit Sales UnitSoldIncludngThisProdTillDate
|
2014 Jan P1 | 4 4
P2 | 2 6
P3 | 0 6
Feb P1 | 1 7
P2 | 0 7
P3 | 3 10
2015 Jan P1 | 7 17
...... and so on
Without the cross join with Product, the query runs fine.
However, joining with Product does not give what I want.
How to solve this problem?
The MDX query that I am running is
AGGREGATE(YTD(), [Measures].[Unit Sales])
SELECT {[Measures].[Unit Sales], MEASURES.YTDDEMO} ON 0,
NON EMPTY {[Time].[Month].Members * [Product].[ProductLevel].Members} ON 1
FROM [TestFoodMart]
Added on 10th June 2015
I have been using the correct syntax etc.
Without the Join I get the following:
| | Unit Sales | Sales To Date |
+------+-----------+------------+---------------+
| 2013 | April | 45,049 | 45,049 |
| | August | 44,777 | 89,826 |
| | December | | 89,826 |
| | February | 44,431 | 134,257 |
| | January | 46,313 | 180,570 |
| | July | 46,671 | 227,241 |
| | June | 45,611 | 272,852 |
| | March | 46,334 | 319,186 |
| | May | 45,085 | 364,271 |
| | November | 53,807 | 418,078 |
| | October | 43,945 | 462,023 |
| | September | 47,964 | 509,987 |
With the Join I get the following:
| Unit Sales | YTDDEMO |
+------+-----------+---------------------------------------------+------------+---------+
| 2013 | April | ADJ Rosy Sunglasses | 38 | 38 |
| | | Akron City Map | 29 | 29 |
| | | Akron Eyeglass Screwdriver | 34 | 34 |
| | | American Beef Bologna | 28 | 28 |
| | | American Chicken Hot Dogs | 25 | 25 |
|
As you can see, the aggregation is not working
You're missing the WITH line of code:
WITH MEMBER MEASURES.YTDDEMO AS
AGGREGATE(YTD(), [Measures].[Unit Sales])
SELECT
{[Measures].[Unit Sales], MEASURES.YTDDEMO} ON 0,
NON EMPTY
{[Time].[Month].Members * [Product].[ProductLevel].Members} ON 1
FROM [TestFoodMart]
Please be aware of the Remarks section in the MSDN definition of the YTD function: https://msdn.microsoft.com/en-us/library/ms146039.aspx
If a member expression is not specified, the default is the current
member of the first hierarchy with a level of type Years in the first
dimension of type Time in the measure group. The Ytd function is a
shortcut function for the PeriodsToDate function where the Type
property of the attribute hierarchy on which the level is based is set
to Years. That is, Ytd(Member_Expression) is equivalent to
PeriodsToDate(Year_Level_Expression,Member_Expression). Note that this
function will not work when the Type property is set to FiscalYears.
If the type property of your year attribute hierarchy is not set to time, then the YTD function will not work.
Does this equivalent version work?
WITH
MEMBER MEASURES.YTDDEMO AS
Aggregate
(
PeriodsToDate([Time].[Year]) <<//change to what your year level is
,[Measures].[Unit Sales]
)
SELECT
NON EMPTY
{
[Measures].[Unit Sales]
,MEASURES.YTDDEMO
} ON 0
,NON EMPTY
{[Time].[Month].Members * [Product].[ProductLevel].Members} ON 1
FROM [Adventure Works];
Could you try this, I think it's only a syntax problem.
AGGREGATE(YTD(), [Measures].[Unit Sales])
SELECT {[Measures].[Unit Sales], MEASURES.YTDDEMO} ON 0,
NON EMPTY ([Time].[Month].Members * [Product].[ProductLevel].Members) ON 1
FROM [TestFoodMart]

Select by increasing order SQL

Table:
id | year | score
-----+------+-----------
12 | 2011 | 0.929
12 | 2014 | 0.933
12 | 2010 | 0.937
12 | 2013 | 0.938
12 | 2009 | 0.97
13 | 2010 | 0.851
13 | 2014 | 0.881
13 | 2011 | 0.885
13 | 2013 | 0.895
13 | 2009 | 0.955
16 | 2009 | 0.867
16 | 2011 | 0.881
16 | 2012 | 0.886
16 | 2013 | 0.897
16 | 2014 | 0.953
Desired Output:
id | year | score
-----+------+-----------
16 | 2009 | 0.867
16 | 2011 | 0.881
16 | 2012 | 0.886
16 | 2013 | 0.897
16 | 2014 | 0.953
I'm having difficulties in trying to output scores that are increasing in respect to the year.
Any help would be greatly appreciated.
So you want to select id = 16 because it is the only one that has steadily increasing values.
Many versions of SQL support lag(), which can help solve this problem. You can determine, for a given id, if all the values are increasing or decreasing by doing:
select id,
(case when min(score - prev_score) < 0 then 'nonincreasing' else 'increasoing' end) as grp
from (select t.*, lag(score) over (partition by id order by year) as prev_score
from table t
) t
group by id;
You can then select all "increasing" ids using a join:
select t.*
from table t join
(select id
from (select t.*, lag(score) over (partition by id order by year) as prev_score
from table t
) t
group by id
having min(score - prev_score) > 0
) inc
on t.id = inc.id;

Access Concatenating Values in a Query

I have a query, [Query1], with employee names, projects, days, months, and years.
In another query, [Query2], I take all the values and put them into a cross table. My rows are "Year, Month, Employee." My column is "Day." My values are Projects.
The problem is that for one date, there may be more than one project assigned to an employee.
When I attempt to put the projects as values into a table using IIf(Count(*)>0,[Project],""), I get an error because there may be more than one possible value for the project, and access doesn't know which one to choose.
I need a way to Concatenate the values if there is more than one Project.
Ex:
[Query1]
Bill | CC555 | 28 | 03 | 2014
Jim | CC999 | 29 | 03 | 2014
Jim | CC555 | 29 | 03 | 2014
John | CC555 | 29 | 03 | 2014
[Query2]
Year | Month | Employee | 1 | 2 | 3 | ... | 27 | 28 | 29 | 30 | 31
2014 | 03 | Bill | - | - | - | ... | - | CC555 | - | - | -
2014 | 03 | Jim | - | - | - | ... | - | - | CC555 + CC999 | - | -
2014 | 03 | John | - | - | - | ... | - | - | CC555 | - | -
Aside: [Query1] is dynamic and could have duplicate dates deleted or added to it, so [Query2] values must change accordingly.
one simple example,you have to make it dynamic,in real scenrio no need of table variable or CTE if using dynamic sql.i think no need of dynamic,just hard code from 1 to 31
;With CTE as
(
select 'Bill' Employee ,'CC555' codes,28 dd,03 mm ,2014 yrs union all
select 'Jim ','CC999', 29 , 03 , 2014 union all
select 'Jim ','CC555', 29 , 03 , 2014 union all
select 'John','CC555', 29 , 03 , 2014
)
select yrs,mm,Employee,isnull([28],'-')[28],[29],[30] from
(select Employee,dd,mm,yrs
,stuff((select ','+codes from cte b where b.Employee=a.Employee for xml path('')),1,1,'')codes
from cte a ) src
pivot (min(codes) for dd in([28],[29],[30])) pvt
By using the function given here allenbrowne.com/func-concat.html, and following the example given here http://www.access-programmers.co.uk/forums/showthread.php?t=234291, I was able to solve the problem.

SQL - How do I query for re-admissions in TSQL?

I'm trying to figure out how to query for readmissions on Server 2008r2. Here is the basic structure of the visit table. There are other fields but none that I thought would be helpful. One issue is that some of these may be transfers instead of discharges which I have no easy way to deduce but that issue can be ignored for now. I tried my hand at this but I guess my understanding of SQL needs more work. I tried to find any info I could online but none of the queries lead me to a useful conclusion or I just didn't understand. Any suggestions would be appreciated.
EDIT: Readmission is if a patient returns within 30 days of previous discharge.
+---------+--------+-----------------+-----------------+
| VisitID | UID | AdmitDT | DischargeDT |
+---------+--------+-----------------+-----------------+
| 12 | 2 | 6/17/2013 6:51 | 6/17/2013 6:51 |
| 16 | 3 | 6/19/2013 4:48 | 6/21/2013 13:35 |
| 18 | 3 | 6/11/2013 12:08 | 6/11/2013 12:08 |
| 21 | 3 | 6/12/2013 14:40 | 6/12/2013 14:40 |
| 22 | 3 | 6/13/2013 10:00 | 6/14/2013 12:00 |
| 25 | 2 | 6/11/2013 16:13 | 6/11/2013 16:13 |
| 30 | 1 | 6/20/2013 8:35 | 6/20/2013 8:35 |
| 31 | 7 | 6/13/2013 6:12 | 6/13/2013 6:12 |
| 34 | 3 | 6/12/2013 8:40 | NULL |
| 35 | 1 | 6/12/2013 8:52 | NULL |
| 38 | 2 | 6/12/2013 10:10 | 6/12/2013 10:10 |
+---------+--------+-----------------+-----------------+
Attempt at Code:
SELECT N2.*
FROM visitTable AS N1
INNER JOIN
visitTable AS N2 ON N1.UID = N2.UID
WHERE N1.EncounterID <> N2.EncounterID AND ( N2.AdmitDT BETWEEN N1.DischargeDT and DATEADD(DD,30, N1.DischargeDT))
Here's a start:
sqlfiddle
new fiddle
It gets each visit for each UID in order of admitDT, then pairs each visit with the next visit in that result. If the current admit date is between the last discharge date and 30 days from then, select it. There are some weird points though - UID 1 is shown to have been admitted on 6/12/2012 and never discharged, but then admitted again on 6/20/2013 and discharged the same day.
edit: restructured a bit to reduce the number of joins
WITH cte AS (
SELECT visitid,uid,dischargedt,admitdt,
row_number()over(partition BY uid ORDER BY admitdt) AS r
FROM t
)
SELECT
c1.visitid AS v1, c2.visitid AS v2,
c1.uid,
c1.dischargedt as [Discharged from first visit],
c2.admitdt as [Admitted to next visit]
FROM cte c1
INNER JOIN cte c2 ON c1.uid=c2.uid
WHERE c1.visitid<>c2.visitid
AND c1.r+1=c2.r
AND c2.admitdt BETWEEN c1.dischargedt AND dateadd(d,30,c1.dischargedt )
ORDER BY c1.uid
Results:
| V1 | V2 | UID | DISCHARGED FROM FIRST VISIT | ADMITTED TO NEXT VISIT |
|----|----|-----|-----------------------------|-----------------------------|
| 25 | 38 | 2 | June, 11 2013 16:13:00+0000 | June, 12 2013 10:10:00+0000 |
| 38 | 12 | 2 | June, 12 2013 10:10:00+0000 | June, 17 2013 06:51:00+0000 |
| 18 | 34 | 3 | June, 11 2013 12:08:00+0000 | June, 12 2013 08:40:00+0000 |
| 21 | 22 | 3 | June, 12 2013 14:40:00+0000 | June, 13 2013 10:00:00+0000 |
| 22 | 16 | 3 | June, 14 2013 12:00:00+0000 | June, 19 2013 04:48:00+0000 |
try this: (Show me the visits where the admission date is after discharge for another earlier visit by the same patient)
Select * From visits v
Where Exists (Select * From Visits
Where uid = v.uid
and v.AdmitDT > DischargeDT)
You have not explained any business rules so I'll take a guess. A readmission is when multiple UID appear, and it is every record except the first one
Here is another method using windowing functions.
SELECT VT.*
FROM visitTable VT
INNER JOIN
(
SELECT VisitID, ROW_NUMBER() OVER (PARTITION BY UID ORDER BY AdmitDT) VisitCount
FROM visitTable
) RA
ON RA.VisitCount > 1 AND RA.VisitID = VT.VisitID

SQL obtaining the last two digits of integer

I need to obtain the last two digits of an integer. Each element placed in the tables comes as a full year ie. YYYY and I only want the last two digits, so that all the fields show
YEAR
----
09
00
89
where the initialy field was
YEAR
----
2009
2000
1989
EDIT: I get a complaint saying,
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
when i try
select right(cast(year as char),2) from subjects;
Postgres has borrowed (or inherited) the modulus operator from C:
SET search_path='tmp';
CREATE TABLE lutser ( year integer);
INSERT INTO lutser (year)
SELECT generate_series(1991,2012)
;
SELECT year
, year / 100 as c2
, year % 100 AS y2
FROM lutser
;
Result:
CREATE TABLE
INSERT 0 22
year | c2 | y2
------+----+----
1991 | 19 | 91
1992 | 19 | 92
1993 | 19 | 93
1994 | 19 | 94
1995 | 19 | 95
1996 | 19 | 96
1997 | 19 | 97
1998 | 19 | 98
1999 | 19 | 99
2000 | 20 | 0
2001 | 20 | 1
2002 | 20 | 2
2003 | 20 | 3
2004 | 20 | 4
2005 | 20 | 5
2006 | 20 | 6
2007 | 20 | 7
2008 | 20 | 8
2009 | 20 | 9
2010 | 20 | 10
2011 | 20 | 11
2012 | 20 | 12
(22 rows)
select substring(CAST(2012 as CHAR(4)), 3, 2)
I don't know if there is a LEN function on Postgres, but if it does, try this:
select SUBSTRING(year,len(year)-1,len(year))
You can also use below SQL query:
select to_char as year from to_char(current_timestamp, 'YY')
here we use last two digit of year from current_timestamp