How to apply a NTILE function to a SQL column - sql

So im trying to establish quantiles over the number of attentions received by some customers, but leaving NULLs and 0 out of the sample and placing them in the 0 and null quantil before applying NTILES function, I'm using this function:
CREATE FUNCTION dbo.RPS_Ntiles (#data int,#percentil int)
RETURNS TABLE
AS
RETURN
SELECT
NTILE(#percentil) OVER(ORDER BY #data DESC) as QUINTIL
WHERE #data > 0
UNION
SELECT
CASE
WHEN CONVERT(int,#data) = 0 THEN 0
WHEN #data is NULL THEN NULL
END AS QUINTIL
WHERE #data = 0 or #data = NULL
Then I'm applying it using CROSS APPLY
SELECT t.QUINTIL,D.[atenciones]
FROM table as D
CROSS APPLY [dbo].[RPS_NTiles](CONVERT(int,D.[atenciones]),5) as t
ORDER BY D.[atenciones] DESC
The result of this query is effective at leaving 0 and nulls out of the NTILEs function but the rest of the data is placed entirely in just 1 quintil like this:
+------------+---------+--+
| Atenciones | Quintil | |
+------------+---------+--+
| 0 | 0 | |
| 0 | 0 | |
| NULL | NULL | |
| 1 | 1 | |
| 3 | 1 | |
| 4 | 1 | |
| 7 | 1 | |
| 46 | 1 | |
| 59 | 1 | |
+------------+---------+--+
And I'm looking for something like this:
+------------+---------+--+
| Atenciones | Quintil | |
+------------+---------+--+
| 0 | 0 | |
| 0 | 0 | |
| NULL | NULL | |
| 1 | 1 | |
| 3 | 2 | |
| 4 | 2 | |
| 7 | 3 | |
| 46 | 4 | |
| 59 | 5 | |
+------------+---------+--+

Why define your own function? You can express this using case logic:
select (case when Atenciones is null or Atenciones = 0
then Atenciones
else ntile(5) over (partition by (case when Atenciones is null or Atenciones = 0 then 1 else 2 end)
order by Atenciones
)
end)

Related

How to assign duplicate increment in SQL?

While going through SQL columns, if we find text match "NEW" in Calc column, update the incrementing a count starting with 1 in Results column.
It should look like this on the output:
The following uses an id column to resolve the order issue. Replace that with your corresponding expression. This also addresses the requirement to start the display sequence with 1 and also show 0 for the 'NEW' rows.
The SQL (updated):
SELECT logs.*
, CASE WHEN text = 'NEW' THEN 0
ELSE
COALESCE(SUM(CASE WHEN text = 'NEW' THEN 1 END) OVER (PARTITION BY xrank ORDER BY id)+1, 1)
END AS display
FROM logs
ORDER BY id
The result:
+----+-------+------+---------+
| id | xrank | text | display |
+----+-------+------+---------+
| 1 | 1 | A | 1 |
| 2 | 1 | B | 1 |
| 3 | 1 | C | 1 |
| 4 | 1 | NEW | 0 |
| 5 | 1 | D | 2 |
| 6 | 1 | Q | 2 |
| 7 | 1 | B | 2 |
| 8 | 1 | NEW | 0 |
| 9 | 1 | D | 3 |
| 10 | 1 | Z | 3 |
| 11 | 2 | A | 1 |
| 12 | 2 | B | 1 |
| 13 | 2 | C | 1 |
| 14 | 2 | NEW | 0 |
| 15 | 2 | D | 2 |
| 16 | 2 | Q | 2 |
| 17 | 2 | B | 2 |
| 18 | 2 | NEW | 0 |
| 19 | 2 | D | 3 |
| 20 | 2 | Z | 3 |
+----+-------+------+---------+
You need a column that specifies the ordering for the table. With that, just use a cumulative sum:
select t.*,
1 + sum(case when Calc = 'NEW' then 1 else 0 end) over (partition by Rank_Id order by Seq) as display
from t;

T-SQL - subqueries on rows grouped by ID to create a summary table

I have a table "MyTable" with an id and two int fields "A" and "B":
MyTable
+-------+-----+-----+
| ID | A | B |
+-------+-----+-----+
| 99 | 0 | 1 |
| 99 | 0 | 1 |
| 99 | 0 | 0 |
| 99 | 1 | 1 |
| 99 | 0 | 1 |
| 100 | 1 | 0 |
| 100 | 0 | 0 |
| 100 | 0 | 0 |
| 444 | 1 | 0 |
| 88 | 0 | 0 |
| 88 | 0 | 0 |
| 88 | 0 | 1 |
+-------+-----+-----+
I'd like a T-SQL query to return a single row for each distinct id, which contains:
each distinct ID
whether there exists a row for that ID with a non-zero value for "a"
whether there exists a row for that ID with a non-zero value for "b"
like so:
+-------+-----+-----+
| ID | A | B |
+-------+-----+-----+
| 99 | 1 | 1 |
| 100 | 1 | 0 |
| 444 | 1 | 0 |
| 88 | 0 | 1 |
+-------+-----+-----+
I can GROUP BY the ID, but I don't know how to create the joins or subqueries on each group to get the desired result.
select id, max(case when A<>0 then 1 else 0 end)A, max(case when B<>0 then 1 else 0 end)B
from mytable
group by id
Or you can just use since your value is 1 and 0. But if value is other than that please use first query.
select id, max(A)A, max(B)B
from mytable
group by id

SQL - Partition restarted based on a column value

I need to create a new column that restarts at every 0 value of Column Repeated Call of each Customer_ID:
+-------------+---------+----------------------+---------------+
| Customer_ID | Call_ID | Days Since Last Call | Repeated Call |
+-------------+---------+----------------------+---------------+
| 1 | 1 | Null | 0 |
| 1 | 2 | 45 | 0 |
| 1 | 3 | 0 | 1 |
| 1 | 4 | 0 | 1 |
| 1 | 5 | 0 | 1 |
| 1 | 6 | 48 | 0 |
| 1 | 7 | 1 | 1 |
| 2 | 8 | Null | 0 |
| 2 | 9 | 1 | 1 |
+-------------+---------+----------------------+---------------+
In to something like this:
+-------------+---------+----------------------+---------------+-------------+
| Customer_ID | Call_ID | Days Since Last Call | Repeated Call | Order_Group |
+-------------+---------+----------------------+---------------+-------------+
| 1 | 1 | Null | 0 | 1 |
| 1 | 2 | 45 | 0 | 2 |
| 1 | 3 | 0 | 1 | 2 |
| 1 | 4 | 0 | 1 | 2 |
| 1 | 5 | 0 | 1 | 2 |
| 1 | 6 | 48 | 0 | 3 |
| 1 | 7 | 1 | 1 | 3 |
| 2 | 8 | Null | 0 | 1 |
| 2 | 9 | 1 | 1 | 1 |
+-------------+---------+----------------------+---------------+-------------+
Appreciate your suggestion, thanks!
You can use SUM() window function:
select t.*,
sum(case when Repeated_Call = 0 then 1 else 0 end)
over (partition by Customer_ID order by Call_Id) Order_Group
from tablename t
See the demo (for MySql but it is standard SQL).
Results:
| Customer_ID | Call_ID | Days Since Last Call | Repeated_Call | Order_Group |
| ----------- | ------- | -------------------- | ------------- | ----------- |
| 1 | 1 | | 0 | 1 |
| 1 | 2 | 45 | 0 | 2 |
| 1 | 3 | 0 | 1 | 2 |
| 1 | 4 | 0 | 1 | 2 |
| 1 | 5 | 0 | 1 | 2 |
| 1 | 6 | 48 | 0 | 3 |
| 1 | 7 | 1 | 1 | 3 |
| 2 | 8 | | 0 | 1 |
| 2 | 9 | 1 | 1 | 1 |
You can calculation every 0 value in column Repeated Call (for each customer) using window analytic function COUNT with ROWS UNBOUNDED PRECEDING:
SELECT *,
COUNT(CASE WHEN Repeated Call=0 THEN 1 ELSE NULL END )OVER(PARTITION BY Customer_ID
ORDER BY Call_ID ROWS UNBOUNDED PRECEDING)Order_Gr FROM Table

How to set value based on value existence in SQL Server?

I have the following T-SQL code:
select
id,
(case
when n in(Bla1', 'Bla2') then 1
when n = 'Bla3' then 99
else 0
end) as c
from
hello
Running this code outputs this result:
| id | c |
+--------+----+
| 577140 | 0 |
| 577140 | 1 |
| 577140 | 0 |
| 577140 | 0 |
| 577140 | 99 |
| 577141 | 0 |
| 577141 | 0 |
| 577141 | 0 |
| 577142 | 0 |
| 577142 | 0 |
| 577142 | 1 |
How can I modify the code to get the following output?
| id | c |
+--------+----+
| 577140 | 99 |
| 577141 | 0 |
| 577142 | 1 |
Rule
For each id: If 99 exists, then c becomes 99. If not, either 1 or 0, depending if any 1 exists.
You can use aggregation:
select id,
max(case when n in ('Bla1', 'Bla2') then 1
when n = 'Bla3' then 99
else 0
end) as c
from hello
group by id;

TSQL: Track Employee Assignment by Month

I have a query that returns the following table. Null indicates the employee is still in that assignment:
|Dept|EmployeeAssignment|BeginDate |EndDate |
|1003|Analyst |01/01/1990|02/04/2013|
|1002|Coordinator |05/14/2000|06/01/2013|
|1003|Trainer |07/28/2010|NULL |
|1004|Janitor |08/09/2013|NULL |
|1005|IT |09/02/2013|12/21/2013|
Is there anything I can do that would allow me to track if the employee was present in that assignment by month during 2013. Something like this would be ideal:
|Dept|EmployeeAssignment|BeginDate |EndDate |Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec|
|1003|Analyst |01/01/1990|02/04/2013| 1 | 1 | | | | | | | | | | |
|1002|Coordinator |05/14/2000|06/01/2013| 1 | 1 | 1 | 1 | 1 | 1 | | | | | | |
|1003|Trainer |07/28/2010|NULL | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
|1004|Janitor |08/09/2013|NULL | | | | | | | | 1 | 1 | 1 | 1 | 1 |
|1005|IT |09/02/2013|12/21/2013| | | | | | | | | 1 | 1 | 1 | 1 |
Thanks in advance for any help you can provide.
Again you can extrapolate other months, but I think it clear to do this works.
Fiddle
//using your demo input
DECLARE #tbl TABLE(Dept INT,EmployeeAssignment VARCHAR(20),BeginDate DATE,EndDate DATE)
INSERT INTO #tbl VALUES
(1003,'Analyst' ,'01/01/1990','02/04/2013')
,(1002,'Coordinator' ,'05/14/2000','06/01/2013')
,(1003,'Trainer' ,'07/28/2010',NULL )
,(1004,'Janitor' ,'08/09/2013',NULL )
,(1005,'IT' ,'09/02/2013','12/21/2013')
SELECT *
,CASE WHEN BeginDate <= '2013-01-31' AND ISNULL(EndDate,GETDATE()) >='2013-01-01' THEN 1 ELSE NULL END Jan
,CASE WHEN BeginDate <= '2013-02-28' AND ISNULL(EndDate,GETDATE()) >='2013-02-01' THEN 1 ELSE NULL END Feb
FROM #tbl