PostreSQL returning only specific value - sql

I've got below structure:
sample_table
code | delivery | end_date | type
---------------+----------------+--------------+------
C086000-T10001 | OK | 2014-11-12 | 01
C086000-T10001 | OK | 2014-11-11 | 03
C086000-T10002 | FALSE | 2014-12-10 | 03
C086000-T10002 | FALSE | 2014-01-04 | 03
C086000-T10003 | FALSE | 2014-02-28 | 03
C086000-T10003 | FALSE | 2014-11-12 | 01
C086000-T10003 | FALSE | 2014-08-20 | 01
I want to output how many code (counted) has the OK delivery status.
I was trying to do something like:
SELECT sample_table.code AS code, sample_table.delivery AS delivered
FROM sample_table
WHERE COUNT(sample_table.delivery = "OK")
GROUP BY code, delivered
Edit
The expected output should be like below
code | delivered | all_type |
---------------+----------------+--------------+
C086000-T10001 | 2 | 04 |
C086000-T10002 | 0 | 06 |
C086000-T10003 | 0 | 05 |

In Postgres, the filter() clause comes handy for this:
select
code,
count(*) filter(where delivery = 'OK') delivered,
sum(type) all_type
from sample_table
group by code

Try this:
SELECT count(*)
FROM sample_table
WHERE sample_table.delivery = "OK"
If you want it by code:
SELECT code, count(*)
FROM sample_table
WHERE sample_table.delivery = "OK"
group by code

With conditional aggregation:
SELECT code,
SUM(CASE WHEN delivery = 'OK' THEN 1 ELSE 0 END ) AS delivered,
SUM(type) all_type
FROM sample_table
GROUP BY code

Related

SQL Server 2019. Need to update particular column in particular row

I have table like this:
LinN | RecType | Value
-----+---------+--------
1 | 01 | 12345
2 | 02 | Null
3 | 03 | Null
4 | 05 | Null
6 | 01 | 67890
7 | 02 | Null
8 | 09 | Null
I need to make it look like this:
LinN | RecType | Value
-----+---------+--------
1 | 01 | 12345
2 | 02 | 12345
3 | 03 | 12345
4 | 05 | 12345
6 | 01 | 67890
7 | 02 | 67890
8 | 09 | 67890
I tried a cursor, Lead and Lag - but nothing is working for me.
Any idea/help will be highly appreciated
You can use a cumulative max:
select LinN, RecType, Value,
max(value) over (order by try_convert(int, LinN)) as imputed_value
from t;
You can incorporate this into an update:
with toupdate as (
select LinN, RecType, Value, max(value) over (order by LinN) as imputed_value
from t
)
update toupdate
set value = imputed_value;
Moderators, please close this discussion. I do not see any reason to keep it active.
Thank you everybody for your time.

SQL: Get an aggregate (SUM) of a calculation of two fields (DATEDIFF) that has conditional logic (CASE WHEN)

I have a dataset that includes a bunch of stay data (at a hotel). Each row contains a start date and an end date, but no duration field. I need to get a sum of the durations.
Sample Data:
| Stay ID | Client ID | Start Date | End Date |
| 1 | 38 | 01/01/2018 | 01/31/2019 |
| 2 | 16 | 01/03/2019 | 01/07/2019 |
| 3 | 27 | 01/10/2019 | 01/12/2019 |
| 4 | 27 | 05/15/2019 | NULL |
| 5 | 38 | 05/17/2019 | NULL |
There are some added complications:
I am using Crystal Reports and this is a SQL Expression, which obeys slightly different rules. Basically, it returns a single scalar value. Here is some more info: http://www.cogniza.com/wordpress/2005/11/07/crystal-reports-using-sql-expression-fields/
Sometimes, the end date field is blank (they haven't booked out yet). If blank, I would like to replace it with the current timestamp.
I only want to count nights that have occurred in the past year. If the start date of a given stay is more than a year ago, I need to adjust it.
I need to get a sum by Client ID
I'm not actually any good at SQL so all I have is guesswork.
The proper syntax for a Crystal Reports SQL Expression is something like this:
(
SELECT (CASE
WHEN StayDateStart < DATEADD(year,-1,CURRENT_TIMESTAMP) THEN DATEDIFF(day,DATEADD(year,-1,CURRENT_TIMESTAMP),ISNULL(StayDateEnd,CURRENT_TIMESTAMP))
ELSE DATEDIFF(day,StayDateStart,ISNULL(StayDateEnd,CURRENT_TIMESTAMP))
END)
)
And that's giving me the correct value for a single row, if I wanted to do this:
| Stay ID | Client ID | Start Date | End Date | Duration |
| 1 | 38 | 01/01/2018 | 01/31/2019 | 210 | // only days since June 4 2018 are counted
| 2 | 16 | 01/03/2019 | 01/07/2019 | 4 |
| 3 | 27 | 01/10/2019 | 01/12/2019 | 2 |
| 4 | 27 | 05/15/2019 | NULL | 21 |
| 5 | 38 | 05/17/2019 | NULL | 19 |
But I want to get the SUM of Duration per client, so I want this:
| Stay ID | Client ID | Start Date | End Date | Duration |
| 1 | 38 | 01/01/2018 | 01/31/2019 | 229 | // 210+19
| 2 | 16 | 01/03/2019 | 01/07/2019 | 4 |
| 3 | 27 | 01/10/2019 | 01/12/2019 | 23 | // 2+21
| 4 | 27 | 05/15/2019 | NULL | 23 |
| 5 | 38 | 05/17/2019 | NULL | 229 |
I've tried to just wrap a SUM() around my CASE but that doesn't work:
(
SELECT SUM(CASE
WHEN StayDateStart < DATEADD(year,-1,CURRENT_TIMESTAMP) THEN DATEDIFF(day,DATEADD(year,-1,CURRENT_TIMESTAMP),ISNULL(StayDateEnd,CURRENT_TIMESTAMP))
ELSE DATEDIFF(day,StayDateStart,ISNULL(StayDateEnd,CURRENT_TIMESTAMP))
END)
)
It gives me an error that the StayDateEnd is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. But I don't even know what that means, so I'm not sure how to troubleshoot, or where to go from here. And then the next step is to get the SUM by Client ID.
Any help would be greatly appreciated!
Although the explanation and data set are almost impossible to match, I think this is an approximation to what you want.
declare #your_data table (StayId int, ClientId int, StartDate date, EndDate date)
insert into #your_data values
(1,38,'2018-01-01','2019-01-31'),
(2,16,'2019-01-03','2019-01-07'),
(3,27,'2019-01-10','2019-01-12'),
(4,27,'2019-05-15',NULL),
(5,38,'2019-05-17',NULL)
;with data as (
select *,
datediff(day,
case
when datediff(day,StartDate,getdate())>365 then dateadd(year,-1,getdate())
else StartDate
end,
isnull(EndDate,getdate())
) days
from #your_data
)
select *,
sum(days) over (partition by ClientId)
from data
https://rextester.com/HCKOR53440
You need a subquery for sum based on group by client_id and a join between you table the subquery eg:
select Stay_id, client_id, Start_date, End_date, t.sum_duration
from your_table
inner join (
select Client_id,
SUM(CASE
WHEN StayDateStart < DATEADD(year,-1,CURRENT_TIMESTAMP) THEN DATEDIFF(day,DATEADD(year,-1,CURRENT_TIMESTAMP),ISNULL(StayDateEnd,CURRENT_TIMESTAMP))
ELSE DATEDIFF(day,StayDateStart,ISNULL(StayDateEnd,CURRENT_TIMESTAMP))
END) sum_duration
from your_table
group by Client_id
) t on t.Client_id = your_table.client_id

SELECT Top values for each records

I have been battling through this query/query design for sometime now and I thought it's time to ask the experts! Here's my table results:
ID | Status | date |
---------------------------------
05 | Returned | 20/6/2018 |
03 | Sent | 12/5/2018 |
01 | Pending | 07/6/2018 |
01 | Engaged | 11/4/2018 |
03 | Contacted | 16/4/2018 |
05 | Surveyed | 04/3/2017 |
05 | No Contact | 05/3/2017 |
How do I get it to return top/newest value for each ID:
ID | Status | date |
---------------------------------
05 | Returned | 20/6/2018 |
03 | Sent | 12/5/2018 |
01 | Pending | 07/6/2018 |
I've tried group by, TOP 1, Distinct and results still not what I wanted. Also, displaying the results by top 5% is won't do either as the ID can be more than just 3 types.
My QUERY below:
INSERT INTO TmpAllcomsEmployee ( StatusID, EmployeeID, CommunicationDate )
SELECT DISTINCT CommunicationLog.StatusID, TmpAllcomsEmployee.EmployeeID,
Max(CommunicationLog.CommunicationDate) AS MaxOfCommunicationDate
FROM CommunicationLog RIGHT JOIN TmpAllcomsEmployee ON
CommunicationLog.EmployeeID = TmpAllcomsEmployee.EmployeeID
GROUP BY CommunicationLog.StatusID, TmpAllcomsEmployee.EmployeeID
ORDER BY Max(CommunicationLog.CommunicationDate) DESC;
One method is a correlated subquery:
select cl.*
from CommunicationLog as cl
where cl.date = (select max(cl2.date)
from CommunicationLog as cl2
where cl2.EmployeeID = cl.EmployeeID
);
This gets the most recent record for each employee in CommunicationLog. You can join in the other table if you really need it. It does not seem unnecessary unless you are using it for filtering.

TSQL : conditional query

I have the following table :
| RoomID | OrderID | Occupancy | rn |
+--------+---------+-----------+----+
| 01 | 101 | Vacant | 1 |
| 01 | 102 | Occupied | 2 |
| 01 | 103 | Occupied | 3 |
| 01 | 104 | Vacant | 4 |
| 02 | 201 | Vacant | 1 |
| 02 | 202 | Occupied | 2 |
| 02 | 203 | Vacant | 3 |
| 03 | 301 | Vacant | 1 |
| 03 | 302 | Occupied | 2 |
| 03 | 303 | Occupied | 3 |
| 03 | 304 | Occupied | 4 |
| 04 | 401 | Occupied | 1 |
| 04 | 402 | Occupied | 2 |
| 04 | 403 | Vacant | 3 |
| 04 | 404 | Occupied | 4 |
I need to flag the RoomIDs where all of the following requirments are met as 'Yes' and if one or more requirements are not met as 'No':
when rn = 1 the Occupancy is vacant
When rn = 2 the Occupancy is Occupied
Any rn larger than 2 (3,4,5..) has an Occupancy of vacant
The result should look like the following:
| RoomID | OrderID |
+--------+---------+
| 01 | Yes |
| 02 | Yes |
| 03 | No |
| 04 | No |
I have an impression that this is easy but I cannot see it at the moment, thank you in advance for your help !
Builds on 'Yes' > 'No'
select RoomId,
min(case when rn<>2 and Occupancy='vacant'
or rn=2 and Occupancy='Occupied'
then 'Yes' else 'No' end) res
from myTable
group by RoomId
Using a CASE to calculate the rule for each row.
When the minimum of that rule (grouped by room_id) is 0, then at least 1 of them didn't follow the rule.
SELECT RoomID,
IIF(MIN(
CASE
WHEN rn <> 2 AND Occupancy = 'Vacant' THEN 1
WHEN rn = 2 AND Occupancy = 'Occupied' THEN 1
ELSE 0
END)=0,'No','Yes') as OccupancyRule
FROM RoomOccupancyTable
GROUP BY RoomID;
You can use Conditional Aggregation to check each condition.
with roomquery as (
select RoomId,
min(case when rn<>2 and Occupancy='vacant' then 1 else 0 end) cond1,
min(case when rn=2 and Occupancy='Occupied' then 1 else 0 end) cond2
from rooms r
group by RoomId)
select RoomId,
case when Cond1=1 and Cond2=1 then 'Yes' else 'No' end Status
from roomquery
Note: you can rewrite it and use only one condition or eve remove the CTE (with statement) altogether, but I believe this is more readable and maintainable.
I guess this will help you.
SELECT RoomID,
CASE WHEN ((rn = 1 OR rn > 2) AND Occupancy = 'Vacant') AND (rn = 2 AND Occupancy = 'Occupied') THEN 'Yes' ELSE 'No' END
FROM Table_Name
Here is another answer that counts the number of violations per RoomID in a subquery and then prints "yes" or "no" accordingly:
SELECT sub.roomid,
CASE WHEN sum(sub.orderid_no) IS NOT NULL THEN 'No'
ELSE 'Yes'
END AS orderID
FROM
(SELECT roomid,
CASE WHEN rn = 1 AND occupancy = 'occupied' THEN 1
WHEN rn = 2 AND occupancy = 'vacant' THEN 1
WHEN rn > 2 AND occupancy = 'occupied' THEN 1
END AS orderid_no
FROM T) sub
GROUP BY sub.roomid
Tested here: http://sqlfiddle.com/#!9/d9d467/14
Also, I think your output above is mistaken. According to your data, RoomID 01 should have OrderID "No"

convert a subquery to over

You can convert this query for example to optimize over for a consultation, is to get you up depending on the version, it's the same table.
select MAX(y.Version),
y.Unidad,
y.RutCompañia,
y.Cobertura,
y.Temporada,
y.PorcentajeSubsidio,y.RendimientoInferior,y.RendimientoSuperior
from ddbb.dbo.NS_CA y
where y.IDVariedad='010101' and y.Temporada ='2011'
and y.ZHS='CA0607'
and y.Moneda='UF'
and y.Version in (select MAX(x.Version) from ddbb.dbo.NS_CA x where x.IDVariedad='010101' and x.Temporada ='2011'
and x.ZHS='CA0607'
and x.Moneda='UF')
group by y.Unidad,y.RutCompañia,y.Cobertura,y.Temporada,y.PorcentajeSubsidio,y.RendimientoInferior,y.RendimientoSuperior
I imagined something like this
select MAX(b.Version) OVER(PARTITION BY b.IDVariedad,b.IDRubro ) as maximo
but it does not work properly
Thank you.
EDIT:
Thanks for you translate and answers.
Add more info , by example i have next table (tabla) :
| Version | Temporada | Unidad | etc |
|:-----------|------------:|:------------:|:------------:|
| 00 | 2011 | N | xx |
| 00 | 2011 | N | xx |
| 01 | 2011 | N | xx |
| 02 | 2011 | N | xx |
| 03 | 2011 | N | xx |
| 03 | 2011 | N | xx |
and query i would generate is :
select * from tabla a
where a.version in (select max(b.Version) from tabla b where b.Temporada='2011')
| Version | Temporada | Unidad | etc |
| 03 | 2011 | N | xx |
| 03 | 2011 | N | xx |
is possibility change sub-query to 'over partition' ? , thanks
WITH cte as
(SELECT ROW_NUMBER() OVER (ORDER by y.Version DESC) row_id
, y.Unidad
, y.RutCompañia
, y.Cobertura
, y.Temporada
, y.PorcentajeSubsidio
, y.RendimientoInferior
, y.RendimientoSuperior
FROM ddbb.dbo.NS_CA y
WHERE y.IDVariedad = '010101'
AND y.Temporada = '2011'
AND y.ZHS = 'CA0607'
AND y.Moneda = 'UF'
AND y.Version)
SELECT * FROM cte
where row_id = 1
Seems you want this:
select MAX(y.Version),
y.Unidad,
y.RutCompañia,
y.Cobertura,
y.Temporada,
y.PorcentajeSubsidio,y.RendimientoInferior,y.RendimientoSuperior
from y.Version =
(select MAX(x.Version)
from ddbb.dbo.NS_CA x
where x.IDVariedad=y.IDVariedad and x.Temporada = y.Temporada
and x.ZHS=y.ZHS and x.Moneda=y.Moneda)
group by y.Unidad,y.RutCompañia,y.Cobertura,y.Temporada,y.PorcentajeSubsidio,y.RendimientoInferior,y.RendimientoSuperior
See Why no windowed functions in where clauses?