Count number of rows in group even if there is zero? - sql

If I have a relation collab(a1, a2) with rows
(a1, a2)
1, 2
1, 3
1, 3
2, 4
and another relation ident(a1)
with rows
(a1)
1,
2,
3,
4,
Then I can I, for each value of a1 in ident, extract a1 and the count number of a2s that are matched with this particular value of a1?
Thus, I want the result
(a1, num_a2)
1, 2
2, 1
3, 0
4, 0

If I understand your requirement correct, this following query should help you getting your required output-
Demo Here
SELECT a1, count(a11)
FROM
(
SELECT table_2.a1, table_1.a1 a11
FROM table_2
LEFT JOIN table_1 ON table_2.a1 = table_1.a1
)A
GROUP BY a1
As #GMB said, you no sub query is required as directly this can be achieved as below-
Demo Here
SELECT table_2.a1, COUNT(table_1.a1)
FROM table_2
LEFT JOIN table_1 ON table_2.a1 = table_1.a1
GROUP BY table_2.a1

I don´t know if I understood correctly. But here´s my little contribution:
You can use the function "COALESCE" to replace missing values with a zero. In this case you select your ID list from the table IDENT and left join the table collab with the number count (distinct in your case, as you want unique numbers, like in your output) and replace all NA´s with zeros:
SELECT t1.a1, COALESCE(t2.NUMBER, 0)
FROM( SELECT a1 FROM ident) t1
LEFT JOIN
( SELECT a1, COUNT (DISTINCT a2) as N FROM collab GROUP BY a1 ) t2

Related

SQL Server : select column with 3 matching characters

I am selecting the records from 2 tables in which the 1st table column named DESC (first 3 characters) should match with the project column of the 2nd table.
I want to get the last 2 characters from Table 1 column DESC to be added in my output, but the last 2 characters are not present in Table 2 column project.
select SUBSTRING(a.[DESC], 1, 3)
from Table1 a
join Table2 b on SUBSTRING(a.[DESC], 1, 3) = b.project
Input: 1st Table DESC Column: Value: '2AB F YY'
2nd Table Project Column: Value: '2AB'
Expected Output: Return all the records of value 2AB
Column result:
'2AB YY'
Wrong output: all the records of value starting other then 2AB
One option is as follows
with data
as (
select SUBSTRING(a.[DESC],1,3) as first_3_characters,
,REVERSE(SUBSTRING(REVERSE(a.[DESC]),1,2)) as last_2_char_tab1
,REVERSE(SUBSTRING(REVERSE(b.project),1,2)) as last_2_char_tab2 characters_tab2
from Table1 a
join Table2 b
on SUBSTRING(a.[DESC],1,3) = b.project
)
select *,CONCAT(first_3_characters,last_2_characters)
from data
where last_2_char_tab1 <> last_2_char_tab2
Since you don't seem to need data from Table2, an EXISTS could be used for this.
And RIGHT can be used to get the last N characters of a string.
SELECT
CONCAT(LEFT([DESC], 3),' ', RIGHT([DESC], 2))
FROM Table1 t1
WHERE EXISTS
(
SELECT 1
FROM Table2 t2
WHERE t2.project = LEFT(t1.[DESC], 3)
)
ORDER BY 1;

Get rows in which any column contains sequence using exists select 1

I have a table with three columns with the following values (dbFiddle)
C1 C2 C3
----------------------------
Red Yellow Blue
null Red Green
Yellow null Violet
I'm trying to create a query that returns all the rows that contain the value "Yellow" without using IN or OR. If I execute the following query:
SELECT 1
FROM test
WHERE CONCAT(C1, C2, C3) LIKE '%Yellow%'
It correctly returns the rows specified. However, if I try to use this query inside an exists:
SELECT *
FROM test
WHERE EXISTS (SELECT 1 FROM test WHERE CONCAT(C1, C2, C3) LIKE '%Yellow%')
it returns all the rows, not just the two with the "Yellow" word. What am I doing wrong here?
Any help would be greatly appreciated.
Re
SELECT 1 FROM test WHERE CONCAT(C1, C2, C3) LIKE '%Yellow%'
"correctly returns the rows specified"
The select returns a single column, 1, although this is because there is a row which has one column containing Yellow somewhere in its text.
This is because EXISTS:
Returns TRUE if a subquery contains any rows.
i.e. All of the following queries also returned all rows in your test table:
SELECT * FROM test WHERE EXISTS (SELECT 1);
SELECT * FROM test WHERE EXISTS (SELECT 0);
SELECT * FROM test WHERE EXISTS (SELECT NULL);
... simply because the SELECT returns at least one row!
The usual usage of EXISTS also includes correlation of the subquery in the EXISTS back to the outer select.
Example of Correlation
In the below contrived example, we've got 4 people living in two houses. Here we're using EXISTS to figure out the names of the persons who are happy, and also have someone else who is also happy living in the same (correlated) House.
CREATE TABLE House
(
HouseId INT PRIMARY KEY,
Name VARCHAR(MAX)
);
CREATE TABLE Person
(
PersonId INT PRIMARY KEY,
HouseId INT FOREIGN KEY REFERENCES HOUSE(HouseId),
Name VARCHAR(MAX),
IsHappy BIT
);
INSERT INTO House(HouseId, Name) VALUES (1, 'House1'), (2, 'House2');
INSERT INTO Person(PersonId, HouseId, Name, IsHappy) VALUES
(1, 1, 'Joe', 0),
(2, 1, 'Jim', 1),
(3, 2, 'Fred', 1),
(4, 2, 'Mary', 1);
SELECT pOuter.Name
FROM Person pOuter
WHERE pOuter.IsHappy = 1
AND EXISTS
(SELECT 1
FROM Person pInner
WHERE pInner.HouseId = pOuter.HouseId
AND pInner.PersonId != pOuter.PersonId
AND pInner.IsHappy = 1);
Returns
Mary
Fred
(There are obviously other ways to find the same result, e.g. finding groupings of House Id where there exists 2 or more happy people, etc)
exists clause check specifies a subquery to test for the existence of rows.
so exists (SELECT 1 FROM test WHERE CONCAT(C1, C2, C3) LIKE '%Yellow%') will always have rows when any column contain yellow data.
if you want to use exists you need to set inner exists query CONCAT(t.C1, t.C2, t.C3) by the outer table.
SELECT *
FROM test t
where exists (SELECT 1 FROM test WHERE CONCAT(t.C1, t.C2, t.C3) LIKE '%Yellow%')
You don't need to use exists only set the condition on where
SELECT *
FROM test
where CONCAT(C1, C2, C3) LIKE '%Yellow%'
sqlfiddle
I would use cross apply:
SELECT 1
FROM test t CROSS APPLY
(SELECT COUNT(*) as cnt
FROM (VALLUES (C1), (C2), (C3)) V(C)
WHERE c = 'Yellow'
) v
WHERE cnt > 0;
You can readily adapt this to a subquery:
SELECT . . .
FROM test t
WHERE EXISTS (SELECT 1
FROM (VALLUES (C1), (C2), (C3)) V(C)
WHERE c = 'Yellow'
) ;
Personally, I much prefer the direct comparison of each value to 'Yellow' rather than using LIKE. For instance, this will not match "yellow-green" or any other value where "yellow" is part of the name.
And, just for the record, you can still use boolean logic, even if you don't use OR and IN:
where not (coalesce(c1, '') <> 'Yellow' and
coalesce(c2, '') <> 'Yellow' and
coalesce(c3, '') <> 'Yellow'
)
Technically, this is probably the "simplest" solution to your problem. However, I still prefer the apply method, because the intent is clearer.

get the missing numbers between list of numbers using sql

in my database I have 10 users numbers some of them have been deleted, and when I select the column at shows like this:
missing_user_number:
1,
2,
5,
8,
10,
and I need to know if there is a script that can get the missing numbers like this, I don't want the deleted data back, I just want the missing numbers as an integrs data:
missing_user_number:
3,
4,
6,
7,
9,
In most versions of SQL, it is actually easier to get ranges of missing values, rather than each missing value:
select user_number + 1 as missing_range_start, next_user_number - 1 as missing_range_end
from (select t.*,
lead(user_number) over (order by user_number) as next_user_number
from t
) t
where user_number <> user_number + 1;
Note: This only finds internal missing numbers, as in the example in your question.
You can create an in-line numbers table that contains all 10 user numbers. Then LEFT JOIN your table to it in order to get the missing numbers:
SELECT t1.n AS missing_user_number
FROM (
SELECT 1 AS n UNION ALL SELECT 2 ... SELECT 10
) AS t1
LEFT JOIN mytable AS t2 ON t1.n = t2.user_number
WHERE t2.user_number IS NULL

SQL - Select from column A based on values in column B

Lets say I have a table with 2 columns (a, b) with following values:
a b
--- ---
1 5
1 NULL
2 NULL
2 NULL
3 NULL
My desired output:
a
---
2
3
I want to select only those distinct values from column a for which every single occurrence of this value has NULL in column b. Therefore from my desired output, "1" won't come in because there is a "5" in column b even though there is a NULL for the 2nd occurrence of "1".
How can I do this using a TSQL query?
If I understand correctly, you can do this with group by and having:
select a
from t
group by a
having count(b) = 0;
When you use count() with a column name, it counts the number of non-NULL values. Hence, if all values are NULL, then the value will be zero.
It's fairly simple to do:
SELECT A
FROM table1
GROUP BY A
HAVING COUNT(B) = 0
Grouping by A results in all the rows where the value of A is identical to be transferred into a single row in the output. Adding the HAVING clause enables to filter those grouped rows with an aggregate function. COUNT doesn't count NULL values, so when it's 0, there are no other values in B.
Two more ways to do this:
SELECT a
FROM t
EXCEPT
SELECT a
FROM t
WHERE b IS NOT NULL ;
This would use an index on (a, b):
SELECT a
FROM t
GROUP BY a
WHERE MIN(b) IS NOT NULL ;
Try it like this:
DECLARE #tbl TABLE(a INT, b INT);
INSERT INTO #tbl VALUES(1,5),(1,NULL),(2,NULL),(2,NULL),(3,NULL);
--Your test data
SELECT * FROM #tbl;
--And this is what you want - hopefully...
SELECT DISTINCT tbl.a
FROM #tbl AS tbl
WHERE NOT EXISTS(SELECT * FROM #tbl AS x WHERE x.a=tbl.a AND b IS NOT NULL)
To turn your question on it's head, you want the values from column a where there are no non-null values for that value in column b.
select distinct a
from table1 as t1
where 0 = (select count(*)
from table1 as t2
where t1.a = t2.a
and b is not null)
Sample fiddle is here: http://sqlfiddle.com/#!6/5d1b8/1
This should do it:
SELECT DISTINCT a
FROM t
WHERE b IS NULL
AND a NOT IN (SELECT a FROM t WHERE b IS NOT NULL);

Oracle select to get summary

Say I have two database tables T1 and T2. T1 has some column C1 (among others) with values, say 1, 1, 2, 2, 2, 3, null, null. T2 has column C2 (among others) with values, say 1, 1, 2, 4, 5, 5, null. I wish to get a summary of these two columns, i.e. in one query, if possible, get to know how many times each value (null included) occurred in both columns combined. In this case, 1 occurred 4 times, 2 occurred 4 times, 3 and 4 occurred once, 5 occurred twice and null occurred 3 times.
I do not know in advance all the possible values in the columns.
You need a group by on top of a union all query:
SELECT value, COUNT(*)
FROM (SELECT c1 AS value
FROM t1
UNION ALL
SELECT c2 AS value
FROM t2)
GROUP BY value
Depending your table size, your data distribution and maybe the index eventually available on C1 and C2, you might expect better performances by using a query like the following, as Oracle don't have to build the full union of both table.
SELECT C, SUM(N) FROM
(
SELECT C1 AS C, COUNT(*) AS N FROM T1 GROUP BY C1
UNION ALL
SELECT C2, COUNT(*) FROM T2 GROUP BY C2
)
GROUP BY C;
That being said, YMMV. So if this is critical, I would suggest you to carefully examine the query execution plan in order to choose the "right" solution for your particular case.