Getting distinct rows on SQL query with multiple IIF columns - sql

In SQL Server I am creating a view that shows if a record in table CONTACTS has certain tags in a different table TAGS.
This is my query:
SELECT DISTINCT
contacts.ID, contacts.NAME,
IIF(tags.tag = 'A', 1, 0) as A,
IIF(tags.tag = 'B', 1, 0) as B,
IIF(tags.tag = 'C', 1, 0) as C,
IIF(tags.tag = 'D', 1, 0) as D
FROM
contacts
LEFT JOIN
TAGS ON contacts.ID = TAGS.CONTACT_ID
I would like the results like this:
ID NAME A B C D
------------------------------------
1 BOB 1 0 0 1
1 Charlie 1 0 1 0
but I get
ID NAME A B C D
------------------------------------
1 BOB 1 0 0 0
1 BOB 0 0 0 1
1 Charlie 1 0 0 0
1 Charlie 0 0 1 0
Must be something I overlook, but I can't find it.

You need simply use GROUP BY clause + MAX aggreate instead of DISTINCT
SELECT contacts.ID, contacts.NAME
, MAX(IIF(tags.tag = 'A', 1, 0)) as A
, MAX(IIF(tags.tag = 'B', 1, 0)) as B
, MAX(IIF(tags.tag = 'C', 1, 0)) as C
, MAX(IIF(tags.tag = 'D', 1, 0)) as D
FROM contacts LEFT JOIN
TAGS ON contacts.ID = TAGS.CONTACT_ID
group by
contacts.ID, contacts.NAME
As Gordon Linoff says, use of standard SQL case when is more compatible with all the database (postgres, oracle, sql server ...)
IIF(tags.tag = 'A', 1, 0)
is equivalent to
CASE WHEN tags.tag = 'A' THEN 1 ELSE 0 END

Use group by:
SELECT c.ID, c.NAME,
MAX(CASE WHEN t.tag = 'A' THEN 1 ELSE 0 END) as A,
MAX(CASE WHEN t.tag = 'B' THEN 1 ELSE 0 END) as B,
MAX(CASE WHEN t.tag = 'C' THEN 1 ELSE 0 END) as C,
MAX(CASE WHEN t.tag = 'D' THEN 1 ELSE 0 END) as D
FROM contacts c LEFT JOIN
TAGS t
ON c.ID = t.CONTACT_ID
GROUP BY c.ID, c.NAME;
Note changes the query:
Introduced table aliases. These make the query easier to write and to read.
Removed the SELECT DISTINCT, because you really want a GROUP BY.
Changed IIF() to CASE. I see no reason to use a function designed for backward compatibility to MS Access over the SQL standard function.

SELECT contacts.ID, contacts.NAME
, MAX(IIF(tags.tag = 'A', 1, 0)) as A
, MAX(IIF(tags.tag = 'B', 1, 0)) as B
, MAX(IIF(tags.tag = 'C', 1, 0)) as C
, MAX(IIF(tags.tag = 'D', 1, 0) as D
FROM contacts LEFT JOIN
TAGS ON contacts.ID = TAGS.CONTACT_ID
GROUP BY contacts.ID, contacts.NAME

Related

QUERY ERROR : ORA-00937: not a single-group group function

I'm trying to query the percentage of correct answers in my oracle db
SELECT
(Count(P2.Id) /(
SELECT Count(*) FROM POSTS P WHERE P.OwnerUserId = 1
AND P.PostTypeId = 2)* 100) AS AcceptedPercentage
FROM
POSTS P1
INNER JOIN
POSTS P2 ON P1.AcceptedAnswerId = P2.Id
WHERE
P2.OwnerUserId = 1
AND
P2.PostTypeId = 2;
But it gives me this error, how can I fix this?
I think you can simplify it using a hierarchical query, however, without any sample data or expected output its difficult to confirm what is expected:
SELECT CASE
WHEN num_posts > 0
THEN num_accepted_answers/num_posts*100
END
AS AcceptedPercentage
FROM (
SELECT Count(CASE WHEN LEVEL = 2 THEN 1 END)
AS num_accepted_answers,
Count(CASE WHEN OwnerUserId = 1 AND PostTypeId = 2 THEN 1 END)
AS num_posts
FROM POSTS
WHERE LEVEL <= 2
START WITH
OwnerUserId = 1
AND PostTypeId = 2
CONNECT BY
PRIOR Id = AcceptedAnswerId
)
Which, for the sample data:
CREATE TABLE posts (id, owneruserid, posttypeid, acceptedanswerid) AS
SELECT 1, 1, 2, NULL FROM DUAL UNION ALL
SELECT 2, 2, 2, 1 FROM DUAL UNION ALL
SELECT 3, 1, 2, NULL FROM DUAL UNION ALL
SELECT 4, 2, 2, 3 FROM DUAL;
Outputs:
ACCEPTEDPERCENTAGE
100
For your query, you can move the main aggregation to a sub-query:
SELECT CASE
WHEN (SELECT Count(*) FROM POSTS P WHERE P.OwnerUserId = 1 AND P.PostTypeId = 2) > 0
THEN total
/(SELECT Count(*) FROM POSTS P WHERE P.OwnerUserId = 1 AND P.PostTypeId = 2)
* 100
END
AS AcceptedPercentage
FROM (
SELECT Count(P2.Id) AS total
FROM POSTS P1
INNER JOIN POSTS P2
ON P1.AcceptedAnswerId = P2.Id
WHERE P2.OwnerUserId = 1
AND P2.PostTypeId = 2
);
db<>fiddle here

How can i select row from table according to column values of row in sql server

i want to select row on condition based on column values in sql server please check below example with required result.
WITH allData
AS (
select mlid=1,value=0,checkid=1
union all
select mlid=2,value=6,checkid=2
union all
select mlid=3,value=6,checkid=1
union all
select mlid=4,value=0,checkid=2
)
select * from allData
Result
Mlid Value checked
1 0 1
2 6 2
3 6 1
4 0 2
required result -->
condition:- if checked column values is 1 and values column is 0 than display checked values values 2 rows only
either display checked column values 1
like below result
Mlid value checked
2 6 2
3 6 1
This will work for your sample data, but would fail for pretty much anything else?
WITH allData AS (
SELECT MLID = 1, [VALUE] = 0, CHECKID = 1
UNION ALL
SELECT MLID = 2, [VALUE] = 6, CHECKID = 2
UNION ALL
SELECT MLID = 3, [VALUE] = 6, CHECKID = 1
UNION ALL
SELECT MLID = 4, [VALUE] = 0, CHECKID = 2)
SELECT
CASE WHEN a1.CHECKID = 1 AND a1.VALUE = 0 THEN a2.MLID ELSE a1.MLID END AS MLID,
CASE WHEN a1.CHECKID = 1 AND a1.VALUE = 0 THEN a2.[VALUE] ELSE a1.[VALUE] END AS [VALUE],
CASE WHEN a1.CHECKID = 1 AND a1.VALUE = 0 THEN a2.CHECKID ELSE a1.CHECKID END AS CHECKID
FROM
allData a1
INNER JOIN allData a2 ON a2.MLID = a1.MLID + 1 AND a2.CHECKID = 2
WHERE
a1.CHECKID = 1;
I guess this might get you started on a better query, or even raise some questions about what you actually need, and how these rows are related?

Left outer join of 3 tables

I am trying to get the count of distinct people of the shaded region.
Table structure is as follows:
customer key
A234 1
A345 4
A12 5
A989 6
HIVE Query:
select count(distinct(a.customer))
from (
select *
from cust
where key in (1,2,3)) c
left outer join (
select *
from cust
where key in (4,5)) a on a.customer= c.customer where c.customer is null
join
(select *
from cust
where key in (6,7,8,9)) d on c.customer = d.customer and d.customer is null;
Error:
missing EOF at 'join' near 'null'
You have a syntax problem because where follows the from clause, and a given select has only one where.
I would just use group by and having. To get the customers:
select c.customer
from cust c
group by c.customer
having sum(case when key in (1, 2, 3) then 1 else 0 end) > 0 and
sum(case when key in (4, 5, 6, 7, 8, 9) then 1 else 0 end) = 0;
You can then count them with a subquery:
select count(*)
from (select c.customer
from cust c
group by c.customer
having sum(case when key in (1, 2, 3) then 1 else 0 end) > 0 and
sum(case when key in (4, 5, 6, 7, 8, 9) then 1 else 0 end) = 0
) c

Grouping and counting issue

I have a table like this:
Id,Code, (some more columns)
1, c
1, a
1, b
1, b
1, b
2, a -- the desired row
3, b
3, c
3, a
3, a
I want to get one Id (or all) which have only been associated with 'a' and not 'b' and 'c'. How do I do this ?
What i tried just now:
select *
from
(
select Id, count(case when Code='a' then 0 else 1 end) c
from tbl
group by Id
)
where c = 0
Why does this not work ?
This will get you the list of Id values which are associated only with codes of 'a'.
select Id
from tbl
group by Id
having max(case when Code='a' then 0 else 1 end) = 0
See this fiddle for a live demo.

Getting record with the only document by priority issue

I have two tables:
contacts:
id, name
1 Alex
2 John
documents:
id, contactID, type
1 1 1
2 1 2
...
30 1 3
31 2 1
32 2 3
I want to get contact name, and document type. the only record for each contact. Contact may have several document types (the only document per type), and I have the following priority for document types: 2, 3, 1.
For example, Alex has documents of all types, but I should get only:
Alex, 2
John has document types = 1, 3, as a result I should get:
John, 3
Oracle database. But if you'll be able to give sql standard solution, it would be great
Oracle 9i+, use:
WITH example AS (
SELECT c.name,
d.type,
ROW_NUMBER() OVER (PARTITION BY c.id
ORDER BY CASE d.type
WHEN 2 THEN 1
WHEN 3 THEN 2
WHEN 1 THEN 3
ELSE 4
END) AS rnk
FROM CONTACTS c
JOIN DOCUMENTS d ON d.contactid = c.id)
SELECT e.name, e.type
FROM example e
WHERE e.rnk = 1
...or the non-Subquery Factoring (AKA CTE) version (still 9i+):
SELECT e.name, e.type
FROM (SELECT c.name,
d.type,
ROW_NUMBER() OVER (PARTITION BY c.id
ORDER BY CASE d.type
WHEN 2 THEN 1
WHEN 3 THEN 2
WHEN 1 THEN 3
ELSE 4
END) AS rnk
FROM CONTACTS c
JOIN DOCUMENTS d ON d.contactid = c.id) e
WHERE e.rnk = 1
Use an inline view where you map types to priorities, then group by contact, then map priorities back to types.
SELECT C.name
DECODE( V.priority, 'A', 2, 'B', 3, 'C', 1 ) AS type
FROM contacts C
, ( SELECT D.contactid
, MIN( DECODE( D.type, 2, 'A', 3, 'B', 1, 'C' ) ) AS priority
FROM documents D
GROUP BY D.contact_id
) V
WHERE V.contactid = C.id