SQL Grouping entries with a different value - sql

Let's assume I have a report that displays an ID and VALUE from different tables
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 1 | 0 |
3 | 1 | 1 |
4 | 2 | 0 |
5 | 2 | 0 |
My goal is to display this table with grouped IDs and VALUEs. My rule to grouping VALUEs would be "If VALUE contains atleast one '1' then display '1' otherwise display '0'".
My current SQL is (simplified)
SELECT
TABLE_A.ID,
CASE
WHEN TABLE_B.VALUE = 1 OR TABLE_C.VALUE NOT IN (0,1,2,3)
THEN 1
ELSE 0
END AS VALUE
FROM TABLE_A, TABLE_B, TABLE_C
GROUP BY
TABLE_A.ID
(CASE
WHEN TABLE_B.VALUE = 1 OR TABLE_C.VALUE NOT IN (0,1,2,3)
THEN 1
ELSE 0
END)
The output is following
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 1 | 0 |
3 | 2 | 0 |
Which is half way to the output I want
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 2 | 0 |
So my Question is: How do I extend my current SQL (or change it completely) to get my desired output?

If you are having only 0 and 1 as distinct values in FOREIGN_VALUE column then using max() function as mentioned by HoneyBadger in the comment will fulfill your requirement.
SELECT
ID,
MAX(FOREIGN_VALUE) AS VALUE
FROM (SELECT
ID,
CASE WHEN FOREIGN_VALUE = 1
THEN 1
ELSE 0
END AS FOREIGN_VALUE
FROM TABLE,
FOREIGN_TABLE)
GROUP BY
ID;

Assuming value is always 0 or 1, you can do:
select id, max(value) as value
from t
group by id;
If value can take on other values:
select id,
max(case when value = 1 then 1 else 0 end) as value
from t
group by id;

Related

SQL to get count of distinct rows based on different rules

Say you have a table like:
| key | status |
| --- | ------ |
| 3 | A |
| 4 | A |
| 4 | C |
| 5 | B |
| 6 | B |
| 6 | C |
| 7 | A |
| 7 | B |
I want a query that returns, in a single row, the count of the number of rows that contain a specific status, but applying some priority rules. The rules would be different for each row and something like:
Column a_count = count of any distinct key that has a status of A
Column b_count = count of any distinct key that has a status of B, but where the same key does not also appear with a status of A
Column c_count = count of any distinct key that has a status of C, but where the same key does not also appear with a status of A or B
The point being that the total of all counts should equal the total number of distinct keys in the source table. In my sample data above, the results should be:
| a_count | b_count | c_count |
| ------- | ------- | ------- |
| 3 | 2 | 0 |
should be able to do your pivot with case statements and not exists.
SELECT Count (CASE
WHEN status = 'A' THEN 1
ELSE 0
END) AS a_count,
Count (CASE
WHEN status = 'B'
AND NOT EXISTS (SELECT 1
FROM mytable b
WHERE a.KEY = b.KEY
AND b.status = 'A') THEN 1
ELSE 0
END) AS b_count,
Count (CASE
WHEN status = 'C'
AND NOT EXISTS (SELECT 1
FROM mytable c
WHERE a.KEY = c.KEY
AND c.status IN ( 'A', 'B' )) THEN 1
ELSE 0
END) AS c_count
FROM mytable a

Return all rows from a table and indicate with a new column whether they exist or not in another table

If I have 2 tables:
TABLE_SEARCHFIELDS:
FieldID | FieldName
--------------------
1 | MyField1
2 | MyField2
3 | MyField3
4 | MyField4
5 | MyField5
and
TABLE_CUSTOMSEARCHFIELDS:
UserID | FieldID
--------------------
1 | 1
1 | 2
1 | 5
2 | 2
2 | 4
2 | 5
and I would like to return all of the Searchfields from the first table, but would also like indicated whether that Searchfield is active for a particular user.
E.g. I want to query UserID = 1 and get the result:
FieldID | FieldName | Active
------------------------------
1 | MyField1 | 1
2 | MyField2 | 1
3 | MyField3 | 0
4 | MyField4 | 0
5 | MyField5 | 1
What is the best way to achieve this?
I would do this using exists:
select sf.*,
(case when exists (select 1
from customsearchfields csf
where csf.userid = 1 and csf.fieldid = sf.fieldid
)
then 1 else 0
end) as Active
from searchfields sf;
Assuming you have no duplicate rows, you can also do this using a left join:
select sf.*, (case when csf.userid is not null then 1 else 0 end) as Active
from searchfields sf left join
customsearchfields csf
on csf.userid = 1 and csf.fieldid = sf.fieldid;

Increment variable in sql query

Using SQL Server 2008, I want to query a table like so:
| ID | Number
-------------
| 1 | 0
| 2 | 0
| 3 | 1
| 4 | 0
| 5 | 0
| 6 | 1
| 7 | 1
| 8 | 1
The result should be the same table with an additional column that counts.
The method of counting is: if the number in "number" equals to 1 - increment the counter by one for the next line.
An example of result for the provided table:
| ID | Number | Counter
-----------------------
| 1 | 0 | 1
| 2 | 0 | 1
| 3 | 1 | 1
| 4 | 0 | 2
| 5 | 0 | 2
| 6 | 1 | 2
| 7 | 1 | 3
| 8 | 1 | 4
How can this be achieved?
select [ID], [Number],
isnull(1+(select sum([Number]) from Table1 t2 where t2.ID<t1.Id),1)
from Table1 t1
SQL Fiddle to test
This is not too hard to do. What you are looking for is very much like the running total, which you get with sum and a windowing clause.
select id, num, 1 + sum(num) over (order by id) - num as counter
from mytable
order by id;
Here is an SQL fiddle: http://sqlfiddle.com/#!4/958e2a/1.
You can use recursive select too but it is a bit complicated but if you insert other numbers which are greater than 1 it work fine:
with tab(id,number,counter,rn) as
(select t.*,1 as counter,1 as rn from table1 t where id = 1
union all
select t.*,case when t.number = 1 then counter + 1 else counter end as counter,
rn + 1 as rn from table1 t,tab where t.id = tab.rn + 1),
tab2 as (select id,number,counter from tab)
select id,number,case when number = 1 then counter - 1
else counter end as counter from tab2;
SQL Fiddle

Finding records sets with GROUP BY and SUM

I'd like to do a query for every GroupID (which always come in pairs) in which both entries have a value of 1 for HasData.
|GroupID | HasData |
|--------|---------|
| 1 | 1 |
| 1 | 1 |
| 2 | 0 |
| 2 | 1 |
| 3 | 0 |
| 3 | 0 |
| 4 | 1 |
| 4 | 1 |
So the result would be:
1
4
here's what I'm trying, but I can't seem to get it right. Whenever I do a GROUP BY on the GroupID then I only have access to that in the selector
SELECT GroupID
FROM Table
GROUP BY GroupID, HasData
HAVING SUM(HasData) = 2
But I get the following error message because HasData is acutally a bit:
Operand data type bit is invalid for sum operator.
Can I do a count of two where both records are true?
just exclude those group ID's that have a record where HasData = 0.
select distinct a.groupID
from table1 a
where not exists(select * from table1 b where b.HasData = 0 and b.groupID = a.groupID)
You can use the having clause to check that all values are 1:
select GroupId
from table
group by GroupId
having sum(cast(HasData as int)) = 2
That is, simply remove the HasData column from the group by columns and then check on it.
One more option
SELECT GroupID
FROM table
WHERE HasData <> 0
GROUP BY GroupID
HAVING COUNT(*) > 1

Pivoting in Sybase SQL Query?

I am looking for a way to pivot the following results...
ID | Group_Level | Group_Values
1 | Division | Value 1
2 | Department | Value 2
3 | Class | Value 3
Into the following structure....
ID | Division | Department | Class
1 | Value 1 | Value 2 | Value 3
2 | Value 1 | Value 2 | Value 3
The number of columns is fixed (it will always be division/department/class). The query is intended for Sybase... have been unable to figure out how to achieve this sort of pivoting yet. Any advice?
The classic way to pivot to a fixed number of columns is like this:
select id,
max (case when group_level = 'Division' then Group_Values else null end) Division,
max (case when group_level = 'Department' then Group_Values else null end) Department,
max (case when group_level = 'Class' then Group_Values else null end) Class
from
YourTable
group by id
You need some key to define the set of 3 rows. Then, you just self JOIN
So for data like this...
ID | GroupID | Group_Level | Group_Values
1 | 1 | Division | Value 1
2 | 1 | Department | Value 2
3 | 1 | Class | Value 3
4 | 2 | Division | Value 1
5 | 2 | Department | Value 2
6 | 2 | Class | Value 3
you'd have
SELECT
Div.GroupID, Div.Group_Values, Dept.Group_Values, Cl.Group_Values
FROM
MyTable Div
JOIN
MyTable Dept ON Div.GroupID = Dept.GroupID
JOIN
MyTable Cl ON Div.GroupID = Cl.GroupID
WHERE
Div.Group_Level = 'Division'
AND
Dept.Group_Level = 'Department'
AND
Cl.Group_Level = 'Class'