Query Results For Consecutive Months In Column Grouped By Value - sql

The following is sample data:
Name | Hours | RDate | Company |
------------------------------------
A |0 |2014-08-01 |W
A |0 |2014-07-01 |W
A |0 |2014-06-01 |W
A |0 |2014-05-01 |W
B |0 |2014-08-01 |X
C |0 |2014-07-01 |Y
C |0 |2014-06-01 |Y
D |0 |2014-08-01 |V
D |0 |2014-07-01 |Z
The following are the results I desire:
Name | Hours | RDate | Company |
------------------------------------
A |0 |2014-08-01 |W
A |0 |2014-07-01 |W
A |0 |2014-06-01 |W
A |0 |2014-05-01 |W
C |0 |2014-07-01 |Y
C |0 |2014-06-01 |Y
So the question is:
How do I get the results only of which RDate is consecutive months in the columns I.e 2014-08-01, 2014-07-01(2014-08-01, 2014-06-01 would not satisfy)for the same name and the same company

I'm thinking this is somewhat a variation of Grouping Islands of Contiguous Dates problem.
;WITH Cte AS(
SELECT *,
RN = DATEADD(MONTH, - ROW_NUMBER() OVER (PARTITION BY Name, Company ORDER BY RDate), RDate)
FROM Test
)
,CteCount AS(
SELECT *,
CC = COUNT(*) OVER(PARTITION BY Name, Company, RN)
FROM Cte
)
SELECT
Name, Hours, RDate, Company
FROM CteCount
WHERE CC > 1
SQL FIDDLE

Although #wewesthemenace answers is way more efficient, I tried to figure out myself with solution I was working on and it works; Keeping previously marked answer as marked because is way better. This actually works as well:
SELECT
one.*
FROM
foo one
INNER JOIN
foo two
ON
(one.Name = two.Name and one.Company = two.Company)
WHERE
CONVERT(int,FORMAT(two.Date, 'yyyyMM')) - CONVERT(int,FORMAT(one.ACSS_Date, 'yyyyMM')) = 1
ORDER BY
one.Name
,one.Date DESC

Related

In PostgreSQL, conditionally count rows

Background
I'm a novice Postgres user running a local server on a Windows 10 machine. I've got a dataset g that looks like this:
+--+---------+----------------+
|id|treatment|outcome_category|
+--+---------+----------------+
|a |1 |cardiovascular |
|a |0 |cardiovascular |
|b |0 |metabolic |
|b |0 |sensory |
|c |1 |NULL |
|c |0 |cardiovascular |
|c |1 |sensory |
|d |1 |NULL |
|d |0 |cns |
+--+---------+----------------+
The Problem
I'd like to get a count of outcome_category by outcome_category for those id who are "ever treated" -- defined as "id's who have any row where treatment=1".
Here's the desired result:
+----------------+---------+
|outcome_category| count |
+----------------+---------+
|cardiovascular | 3 |
|sensory | 1 |
|cns | 1 |
+----------------+---------+
It would be fine if the result had to contain metabolic, like so:
+----------------+---------+
|outcome_category|treatment|
+----------------+---------+
|cardiovascular | 3 |
|metabolic | 0 |
|sensory | 1 |
|cns | 1 |
+----------------+---------+
Obviously I don't need the rows to be in any particular order, though descending would be nice.
What I've tried
Here's a query I've written:
select treatment, outcome_category, sum(outcome_ct)
from (select max(treatment) as treatment,
outcome_category,
count(outcome_category) as outcome_ct
from g
group by outcome_category) as sub
group by outcome_category, sub.treatment;
But it's a mishmash result:
+---------+----------------+---+
|treatment|outcome_category|sum|
+---------+----------------+---+
|1 |cardiovascular |3 |
|1 |sensory |2 |
|0 |metabolic |1 |
|1 |NULL |0 |
|0 |cns |1 |
+---------+----------------+---+
I'm trying to identify the "ever exposed" id's using that first line in the subquery: select max(treatment) as treatment. But I'm not quite getting at the rest of it.
EDIT
I realized that the toy dataset g I originally gave you above doesn't correspond to the idiosyncrasies of my real dataset. I've updated g to reflect that many id's who are "ever treated" won't have a non-null outcome_category next to a row with treatment=1.
Interesting little problem. You can do:
select
outcome_category,
count(x.id) as count
from g
left join (
select distinct id from g where treatment = 1
) x on x.id = g.id
where outcome_category is not null
group by outcome_category
order by count desc
Result:
outcome_category count
----------------- -----
cardiovascular 3
sensory 1
cns 1
metabolic 0
See running example at db<>fiddle.
This would appear to be just a simple aggregation,
select outcome_category, Count(*) count
from t
where treatment=1
group by outcome_category
order by Count(*) desc
Demo fiddle

In SQL, query a table by transposing column results

Background
Forgive the title of this question, as I'm not really sure how to describe what I'm trying to do.
I have a SQL table, d, that looks like this:
+--+---+------------+------------+
|id|sex|event_type_1|event_type_2|
+--+---+------------+------------+
|a |m |1 |1 |
|b |f |0 |1 |
|c |f |1 |0 |
|d |m |0 |1 |
+--+---+------------+------------+
The Problem
I'm trying to write a query that yields the following summary of counts of event_type_1 and event_type_2 cut (grouped?) by sex:
+-------------+-----+-----+
| | m | f |
+-------------+-----+-----+
|event_type_1 | 1 | 1 |
+-------------+-----+-----+
|event_type_2 | 2 | 1 |
+-------------+-----+-----+
The thing is, this seems to involve some kind of transposition of the 2 event_type columns into rows of the query result that I'm not familiar with as a novice SQL user.
What I've tried
I've so far come up with the following query:
SELECT event_type_1, event_type_2, count(sex)
FROM d
group by event_type_1, event_type_2
But that only gives me this:
+------------+------------+-----+
|event_type_1|event_type_2|count|
+------------+------------+-----+
|1 |1 |1 |
|1 |0 |1 |
|0 |1 |2 |
+------------+------------+-----+
You can use a lateral join to unpivot the data. Then use conditional aggregate to calculate m and f:
select v.which,
count(*) filter (where d.sex = 'm') as m,
count(*) filter (where d.sex = 'f') as f
from d cross join lateral
(values (d.event_type_1, 'event_type_1'),
(d.event_type_2, 'event_type_2')
) v(val, which)
where v.val = 1
group by v.which;
Here is a db<>fiddle.

add specific multiple rows (SQL)

I am new to SQL and I want to write a query to add multiple rows in a table.
For example:
Table:
matchid|player1id|player2id|player1score|player2score
101 |20 |10 |0 |100
101 |20 |10 |0 |100
101 |20 |10 |0 |100
201 |20 |10 |645 |0
201 |20 |10 |100 |700
201 |20 |10 |0 |100
Required output:
matchid|player1id|player2id|player1score|player2score
101 |20 |10 |0 |300
201 |20 |10 |745 |800
Note: I have to do this without using GROUP BY
Without using GROUP BY:
SELECT *
FROM (
SELECT DISTINCT matchid, player1id, player2id FROM tbl
) AS t
CROSS APPLY(
SELECT
SUM(player1score), SUM(player2score)
FROM tbl
WHERE
matchid = t.matchid
AND player1id = t.player1id
AND player2id = t.player2id
) AS x(player1score, player2score)
select matchid,player1id,player2id,SUM(player1score) as
player1score,SUM(player2score) as player2score
FROM table1
Group by player1id,player2id, matchid
SELECT
matchid, player1is, player2id,
SUM(player1score) as player1score,
SUM(player2score) as player2score
FROM
tablename
GROUP BY
matchid, player1id, player2id
Does this satisfy the requirement?:
select
matchid, player1id, player2id,
(select sum(player1score from Table t2 where t2.matchid = t.matchid) as player1score,
(select sum(player2score from Table t2 where t2.matchid = t.matchid) as player2score
from
(select distinct matchid, player1id, player2id from Table) t

How to compare each row against each other and get the best result?

Suppose I have a table of values and categories:
+--+-----+---+
|ID|value|cat|
+--+-----+---+
|0 |1 |0 |
+--+-----+---+
|1 |3 |0 |
+--+-----+---+
|2 |2 |1 |
+--+-----+---+
|3 |1.2 |1 |
+--+-----+---+
|4 |1 |1 |
+--+-----+---+
And I want to know, for each row, the ID of the row which matches the value most closely and belongs to the same category, and I also want to know the difference.
So for row ID=0 the correct answer would be ID=1, and the difference value would be 2. The correct output would be this:
+--+----------+----------+
|ID|difference|best match|
+--+----------+----------+
|0 |2 |1 |
+--+----------+----------+
|1 |2 |0 |
+--+----------+----------+
|2 |0.8 |3 |
+--+----------+----------+
|3 |0.2 |4 |
+--+----------+----------+
|4 |0.2 |3 |
+--+----------+----------+
I'm just learning about CROSS JOIN and while I'm sure this can be done I don't really know where to start.
You can do this with a self-join and making use of the ROW_NUMBER() function in conjunction with MIN():
;WITH cte AS (SELECT a.ID aID
,MIN(ABS(a.value - b.value)) diff
,ROW_NUMBER() OVER(PARTITION BY a.ID ORDER BY MIN(ABS(a.value - b.value)))RN
,b.ID bID
FROM Table1 a
JOIN Table1 b
ON a.cat = b.cat
AND a.ID <> b.ID
GROUP BY a.ID,b.ID)
SELECT aID
,diff
,bID Best_Match
FROM cte
WHERE RN = 1
Demo: SQL Fiddle
If you want to return multiple rows in case of a tie, you'd want to use RANK() instead of ROW_NUMBER()

count and distinct over multiple columns

I have a database table containing two costs. I want to find the distinct costs over these two columns. I also want to find the count that these costs appear. The table may look like
|id|cost1|cost2|
|1 |50 |60 |
|2 |20 |50 |
|3 |50 |70 |
|4 |20 |30 |
|5 |50 |60 |
In this case I want a result that is distinct over both columns and count the number of times that appears. So the result I would like is
|distinctCost|count|
|20 |2 |
|30 |1 |
|50 |4 |
|60 |2 |
|70 |1 |
and ideally ordered
|disctinCost1|count|
|50 |4 |
|60 |2 |
|20 |2 |
|70 |1 |
|30 |1 |
I can get the distinct over two columns by doing something like
select DISTINCT c FROM (SELECT cost1 AS c FROM my_costs UNION SELECT cost2 AS c FROM my_costs);
and I can get the count for each column by doing
select cost1, count(*)
from my_costs
group by cost1
order by count(*) desc;
My problem is how can I get the count for both columns? I am stuck on how to do the count over each individual column and then add it up.
Any pointers would be appreciated.
I am using Oracle DB.
Thanks
By combining your two queries..
select cost, count(*)
from
(
SELECT id, cost1 AS cost FROM my_costs
UNION ALL
SELECT id, cost2 AS c FROM my_costs
) v
group by cost
order by count(*) desc;
(If when a row has cost1 and cost2 equal, you want to count it once not twice, change the union all to a union)
You can use the unpivot statement :
select *
from
(
SELECT cost , count(*) as num_of_costs
FROM my_costs
UNPIVOT
(
cost
FOR cost_num IN (cost1,cost2)
)
group by cost
)
order by num_of_costs desc;