Get max for each row between two dates? - sql

learning sql here and I ran into a challenge.
I have the following table:
tbl <- data.frame(
id_name = c("a", "a", "b", "c", "d", "f", "b", "c", "d", "f"),
value = c(1, -1, 1, 1, 1, 1, -1, -1, -1, -1),
score = c(1, 0, 1, 2, 3, 4, 3, 2, 1, 0),
date = as.Date(c("2001-1-1", "2002-1-1", "2003-1-1", "2005-1-1",
"2005-1-1", "2007-1-1", "2008-1-1", "2010-1-1",
"2011-1-1", "2012-1-1"), "%Y-%m-%d")
)
+---------+-------+-------+-----------+
| id_name | value | score | date |
+---------+-------+-------+-----------+
| a | 1 | 1 | 2001-1-1 |
| a | -1 | 0 | 2002-1-1 |
| b | 1 | 1 | 2003-1-1 |
| c | 1 | 2 | 2005-1-1 |
| d | 1 | 3 | 2005-1-1 |
| f | 1 | 4 | 2007-1-1 |
| b | -1 | 3 | 2008-1-1 |
| c | -1 | 2 | 2010-1-1 |
| d | -1 | 1 | 2011-1-1 |
| f | -1 | 0 | 2012-1-1 |
+---------+-------+-------+-----------+
My goal is this:
For each id_name, I'd like to get the first date (in case of tie breakers) of maximum score from the tbl between the dates where the current row = id_name (inclusive)
For example, id_name 'a' should return '2001-1-1' since its score is 1
id_name 'b' should return '2007-1-1' since its score is 4:
+---------+----------+
| id_name | date |
+---------+----------+
| a | 2001-1-1 |
| b | 2007-1-1 |
+---------+----------+
This is what I have thus far,
sqldf("
SELECT
id_name,
date,
score
FROM
tbl As d
WHERE
score = (
SELECT MAX(score)
FROM tbl As b
WHERE
date >= (
SELECT MIN(date)
FROM tbl
WHERE id_name = b.id_name
) AND
date <= (
SELECT MAX(date)
FROM tbl
WHERE id_name = b.id_name
)
)
")
Problem is that it is returning the rows with the global max value irrespective of the current row value
Thanks!

I think a correlated subquery in the WHERE clause will fit the bill here:
SELECT id_name, date
FROM tbl as t1
WHERE score = (SELECT max(score) FROM tbl WHERE id_name = t1.id_name)

Related

Count the number of appearances of char given a ID

I have to perform a query where I can count the number of distinct codes per Id.
|Id | Code
------------
| 1 | C
| 1 | I
| 2 | I
| 2 | C
| 2 | D
| 2 | D
| 3 | C
| 3 | I
| 3 | D
| 4 | I
| 4 | C
| 4 | C
The output should be something like:
|Id | Count | #Code C | #Code I | #Code D
-------------------------------------------
| 1 | 2 | 1 | 1 | 0
| 2 | 3 | 1 | 0 | 2
| 3 | 3 | 1 | 1 | 1
| 4 | 2 | 2 | 1 | 0
Can you give me some advise on this?
This answers the original version of the question.
You are looking for count(distinct):
select id, count(distinct code)
from t
group by id;
If the codes are only to the provided ones, the following query can provide the desired result.
select
pvt.Id,
codes.total As [Count],
COALESCE(C, 0) AS [#Code C],
COALESCE(I, 0) AS [#Code I],
COALESCE(D, 0) AS [#Code D]
from
( select Id, Code, Count(code) cnt
from t
Group by Id, Code) s
PIVOT(MAX(cnt) FOR Code IN ([C], [I], [D])) pvt
join (select Id, count(distinct Code) total from t group by Id) codes on pvt.Id = codes.Id ;
Note: as I can see from sample input data, code 'I' is found in all of Ids. Its count is zero for Id = 3 in the expected output (in the question).
Here is the correct output:
DB Fiddle

SQL select distinct when one column in and another column greater than

Consider the following dataset:
+---------------------+
| ID | NAME | VALUE |
+---------------------+
| 1 | a | 0.2 |
| 1 | b | 8 |
| 1 | c | 3.5 |
| 1 | d | 2.2 |
| 2 | b | 4 |
| 2 | c | 0.5 |
| 2 | d | 6 |
| 3 | a | 2 |
| 3 | b | 4 |
| 3 | c | 3.6 |
| 3 | d | 0.2 |
+---------------------+
I'm tying to develop a sql select statement that returns the top or distinct ID where NAME 'a' and 'b' both exist and both of the corresponding VALUE's are >= '1'. Thus, the desired output would be:
+---------------------+
| ID | NAME | VALUE |
+---------------------+
| 3 | a | 2 |
+----+-------+--------+
Appreciate any assistance anyone can provide.
You can try to use MIN window function and some condition to make it.
SELECT * FROM (
SELECT *,
MIN(CASE WHEN NAME = 'a' THEN [value] end) OVER(PARTITION BY ID) aVal,
MIN(CASE WHEN NAME = 'b' THEN [value] end) OVER(PARTITION BY ID) bVal
FROM T
) t1
WHERE aVal >1 and bVal >1 and aVal = [Value]
sqlfiddle
This seems like a group by and having query:
select id
from t
where name in ('a', 'b')
having count(*) = 2 and
min(value) >= 1;
No subqueries or joins are necessary.
The where clause filters the data to only look at the "a" and "b" records. The count(*) = 2 checks that both exist. If you can have duplicates, then use count(distinct name) = 2.
Then, you want the minimum value to be 1, so that is the final condition.
I am not sure why your desired results have the "a" row, but if you really want it, you can change the select to:
select id, 'a' as name,
max(case when name = 'a' then value end) as value
you can use in and sub-query
select top 1 * from t
where t.id in
(
select id from t
where name in ('a','b')
group by id
having sum(case when value>1 then 1 else 0)>=2
)
order by id

Generate three rows in select query

I have following table
SELECT TableCode, Col1, Col2
FROM TableA
WHERE TableCode = 23
Result of Table:
TableCode | Col1 | Col1
23 | CustCode | QS
23 | CatCode | QS
After that i wrote one query on TableA which return following output
Query :
SELECT TableCode,x.ColCode,
x.ColumnName + '_' + CONVERT(VARCHAR(5), ROW_NUMBER() OVER (PARTITION BY X.COL ORDER BY X.COL)) [ColumnName],X.Values,
ROW_NUMBER() OVER (PARTITION BY X.COL ORDER BY X.COL) [RowNo]
FROM TableA a CROSS APPLY
(SELECT 1 ColCode,'ParaName' ColumnName,Col1 Values
UNION ALL
SELECT 2,'ParaSource',Col2
) x
WHERE TableCode = 23;
Result :
TableCode | ColCode | ColumnName | Values | RowNo
23 | 1 | ParaName_1 | CustCode | 1
23 | 1 | ParaName_2 | CatCode | 2
23 | 2 | ParaSource_1 | QS | 1
23 | 2 | ParaSource_2 | QS | 2
And i required following output:
Required Output :
TableCode | ColCode | ColumnName | Values | RowNo
23 | 1 | ParaName_1 | CustCode | 1
23 | 1 | ParaName_2 | CatCode | 2
23 | 1 | ParaName_3 | Null | 3
23 | 2 | ParaSource_1 | QS | 1
23 | 2 | ParaSource_2 | QS | 2
23 | 2 | ParaSource_3 | null | 3
Using a couple of common table expressions and row_number() along with the table value constructor (values (...),(...))
to cross join numbers 1, 2, and 3 then using a left join to return 3 rows per TableCode even when you do not have three rows in the source table.
;with numbered as (
select *, rn = row_number() over (order by (select 1))
from TableA
where TableCode = 23
)
, cte as (
select distinct tc.TableCode, a.Col1, a.Col2, v.rn
from numbered tc
cross join (values (1),(2),(3)) v (rn)
left join numbered a
on a.TableCode = tc.TableCode
and a.rn = v.rn
)
select
a.TableCode
, x.ColCode
, [ColumnName] = x.ColumnName + '_' + convert(varchar(5),a.rn)
, X.Value
,[RowNo] = a.rn
from cte a
cross apply (values (1,'ParaName',Col1),(2,'ParaSource',Col2))
as x(ColCode, ColumnName, Value)
order by ColCode, RowNo;
rextester demo: http://rextester.com/CJU8986
returns:
+-----------+---------+--------------+----------+-------+
| TableCode | ColCode | ColumnName | Value | RowNo |
+-----------+---------+--------------+----------+-------+
| 23 | 1 | ParaName_1 | CustCode | 1 |
| 23 | 1 | ParaName_2 | CatCode | 2 |
| 23 | 1 | ParaName_3 | NULL | 3 |
| 23 | 2 | ParaSource_1 | QS | 1 |
| 23 | 2 | ParaSource_2 | QS | 2 |
| 23 | 2 | ParaSource_3 | NULL | 3 |
+-----------+---------+--------------+----------+-------+
This would appear to do what you want:
SELECT TableCode, x.ColCode, v.*
FROM TableA a CROSS APPLY
(VALUES (1, 'ParaName-1', Col1, 1),
(2, 'ParaName-2', Col2, 2),
(3, 'ParaName-3', NULL, 2)
) v(ColCode, ColumnName, [Values], RowNo)
WHERE TableCode = 23;
I see no reason to use row_number() when you can just read in the correct values. Also, VALUES is a SQL keyword so it is a really bad column name.

Convert tuple value to column names

Got something like:
+-------+------+-------+
| count | id | grade |
+-------+------+-------+
| 1 | 0 | A |
| 2 | 0 | B |
| 1 | 1 | F |
| 3 | 1 | D |
| 5 | 2 | B |
| 1 | 2 | C |
I need:
+-----+---+----+---+---+---+
| id | A | B | C | D | F |
+-----+---+----+---+---+---+
| 0 | 1 | 2 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 | 1 | 1 |
| 2 | 0 | 5 | 1 | 0 | 0 |
I don't know if I can even do this. I can group by id but how would you read the count value for each grade column?
CREATE TABLE #MyTable(_count INT,id INT , grade VARCHAR(10))
INSERT INTO #MyTable( _count ,id , grade )
SELECT 1,0,'A' UNION ALL
SELECT 2,0,'B' UNION ALL
SELECT 1,1,'F' UNION ALL
SELECT 3,1,'D' UNION ALL
SELECT 5,2,'B' UNION ALL
SELECT 1,2,'C'
SELECT *
FROM
(
SELECT _count ,id ,grade
FROM #MyTable
)A
PIVOT
(
MAX(_count) FOR grade IN ([A],[B],[C],[D],[F])
)P
You need a "pivot" table or "cross-tabulation". You can use a combination of aggregation and CASE statements, or, more elegantly the crosstab() function provided by the additional module tablefunc. All basics here:
PostgreSQL Crosstab Query
Since not all keys in grade have values, you need the 2-parameter form. Like this:
SELECT * FROM crosstab(
'SELECT id, grade, count FROM table ORDER BY 1,2'
, $$SELECT unnest('{A,B,C,D,F}'::text[])$$
) ct(id text, "A" int, "B" int, "C" int, "D" int, "F" int);

How to merge two different rows(how to assign different value is zero)

I am trying to use union for merging two output but these rows value are different.I need different rows value are zero.like output(third) table.I was struggle with pass two days please help me.
Select t1.round,
t1.SC,
t1.ST,
t1.OTHERS,
t2.round_up,
t2.SC_up,
t2.ST_up,
t2.OTHERS_up
From
(Select round as round,
Sum (non_slsc_qty) as SC,
Sum (non_slst_qty) as ST,
Sum (non_slot_qty) as OTHERS
FROM vhn_issue
where (date between '2015-08-01' and '2015-08-31')AND
dvn_cd='15' AND phc_cd='012' AND hsc_cd='05' GROUP BY round) t1
,
(Select round as round_up,
Sum (non_slsc_qty) as SC_up,
Sum (non_slst_qty) as ST_up,
Sum (non_slot_qty) as OTHERS_up,
FROM vhn_issue
where (date between '2015-04-01' and '2015-08-31')AND
dvn_cd='15' AND phc_cd='012' AND hsc_cd='05' GROUP BY round) t2
This first table result
+-----------------------------------+------------+--------+--------
| round | SC | ST | OTHERS |
+-----------------------------------+------------+--------+--------
| 1 | 20 | 30 | 50 |
| | | | |
| | | | |
+-----------------------------------+------------+--------+--------+
This is second table result
+-----------------------------------+------------+--------+----------
| round_up | SC_up | ST_up | OTHERS_up |
+-----------------------------------+------------+--------+-----------
| 1 | 21 | 31 | 51 |
| 3 | 10 | 5 | 2 |
| | | | |
+-----------------------------------+------------+--------+--------+---
I need output like this
+------------+--------+----------------------------------------------
| round_up | SC | ST |OTHERS | SC_up | ST_up |OTHERS_up |
+------------+--------+-----------------------------------------------
| 1 | 20 | 30 | 50 | 21 | 31 | 51 |
| | | | | | | |
| 3 | 0 | 0 | 0 | 10 | 5 | 2 |
+------------+--------+--------+---------------------------------------
You can use WITH Queries (Common Table Expressions) to wrap the two selects and use RIGHT JOIN to get the desired output,COALESCE is used to print 0 instead of NULL.
WITH a
AS (
SELECT round AS round
,Sum(non_slsc_qty) AS SC
,Sum(non_slst_qty) AS ST
,Sum(non_slot_qty) AS OTHERS
FROM vhn_issue
WHERE (
DATE BETWEEN '2015-08-01'
AND '2015-08-31'
)
AND dvn_cd = '15'
AND phc_cd = '012'
AND hsc_cd = '05'
GROUP BY round
)
,b
AS (
SELECT round AS round_up
,Sum(non_slsc_qty) AS SC_up
,Sum(non_slst_qty) AS ST_up
,Sum(non_slot_qty) AS OTHERS_up
,
FROM vhn_issue
WHERE (
DATE BETWEEN '2015-04-01'
AND '2015-08-31'
)
AND dvn_cd = '15'
AND phc_cd = '012'
AND hsc_cd = '05'
GROUP BY round
)
SELECT coalesce(b.round_up, 0) round_up
,coalesce(a.sc, 0) sc
,coalesce(a.st, 0) st
,coalesce(a.others, 0) others
,coalesce(b.sc_up, 0) sc_up
,coalesce(b.st_up, 0) st_up
,coalesce(b.others_up, 0) others_up
FROM a
RIGHT JOIN b ON a.round = b.round_up
WITH Results_CTE AS
(
Select t1.round as round_up ,
t1.SC as SC,
t1.ST as ST,
t1.OTHERS as OTHERS,
0 as SC_up,
0 as ST_up,
0 as OTHERS_up
from round t1
union all
t2.round_up as round_up ,
0 as SC,
0 as ST,
0 as OTHERS,
t2.SC_up,
t2.ST_up,
t2.OTHERS_up from round t2
)
select round_up , sum(SC) as SC,sum (ST) as ST, sum(OTHERS) as OTHERS, sum(SC_up) as SC_up, sum(ST_up) as ST_up, sum(OTHERS_up) as OTHERS_ up
from Results_CTE group by round_up