SQL say that there are no rows with a certain value - sql-server

I'm new with SQL and I need your help.
I have a table with a Name column and a Value column.
This value represents a 3-values enum, let's say Yes, No, and Maybe.
I'm trying to count, for each name, how many times I have each value of the enum. Something like this:
Name1 Yes 3
Name1 No 1
Name1 Maybe 4
Name2 Yes 0
Name2 No 1
Name2 Maybe 2
Name3 Yes 1
Name3 No 0
Name3 Maybe 7
but there are times when I don't have any entry for a given name with a given value, and it doesn't show up. Shortly, when count is 0, I don't get the row.
And I have to put it in a stored procedure.
How can I achieve this?
Thank you all in advance!
P.S. I'm using SQL Server
EDIT:
What I tried is:
SELECT Name,
Value,
COUNT(Value) as ValueCount
from Data
group by Name, Value
order by Name

Use a cross join to generate all the rows. Then use a left join to join in the values for counting:
SELECT n.Name, v.value, COUNT(d.value) as ValueCount
from (select distinct name from Data) n cross join
(select distinct value from Data) v left join
data d
on d.name = n.name and d.value = v.value
group by n.Name, v.Value
order by n.Name;
You don't specify the database. This is standard SQL and should work in any database.

You can use two cte to get a list of all combinations you want to show, then do a left join with your original table.
WITH enum AS
(
SELECT 'Yes' AS Value
UNION
SELECT 'No'
UNION
SELECT 'Maybe'
), combs AS
(
SELECT DISTINCT d.Name, e.Value
FROM enum e, Data d
)
SELECT c.Name, c.Value, COUNT(d.Name) FROM combs c
LEFT JOIN data d ON d.Name = c.Name AND d.Value = c.Value
GROUP BY c.Name, c.Value
ORDER BY c.Name, c.Value

Looking at your table overall, it would be a lot cleaner like this:
Name Yes No Maybe
__________________________
Bob 3 4 0
Tim 0 1 3
Jim 2 7 2
I know that's not really an answer to solving your problems but this table is much smaller and achieves the same results.

Often when trying to count something that isn't there, it's best to join to a driver table, in this case, you need all name/value pairs to join against, created below in a simple cte using a CROSS JOIN, but you could pull the values in a number of ways:
;with cte AS (SELECT DISTINCT d1.name,d2.value
FROM data d1
CROSS JOIN data d2
)
SELECT cte.Name,
cte.Value,
COALESCE(COUNT(d.Value),0) as ValueCount
from cte
LEFT JOIN Data d
ON d.value = cte.value
AND d.name = cte.name
group by cte.Name, cte.Value
order by cte.Name
Since this requires a LEFT JOIN any missing values will return NULL, so COALESCE() is used to show 0.

Related

Need guidance in forming a query in snowflake

SELECT id,
login_id,
count,
case when count = 0 then 'Cat_A'
WHEN count between 1 and 10 then 'Cat_B'
WHEN count > 10 then 'Cat_C'
WHEN count IS NULL THEN 'Cat D'
END as Category
FROM
(
select id,login_id,min(ord_count) AS count
FROM table_1 X
JOIN table_2 Y
ON X.id_col = Y.id_col
WHERE date = '2022-02-02'
AND login_id = 'True'
group by id,login_id
)A
LEFT JOIN
(
SELECT id,COUNT(X.ord_no) AS count_of_orders
FROM table_1 X
WHERE X.date = '2022-02-02'
group by id
)B
ON A.id=B.id
When I join these two tables, I'm getting NULL values for the unmatched records.
I need to replace those NULL records to some hardcoded value say 'XYZ'.
Any guidance on how to achieve this please?
So the top level select needs to name which ID it is using (other DB's don't require this snowflake does), given you are selecting from A and b.id might be missing, it should be a.id
count_of_orders is not used, so currently the LEFT JOIN to B is pointless, given your question is about LEFT JOIN this must be the column you a referring to??
The replace NULL values can be done via COALESCE or NVL or ZEROIFNULL, given the only null thing is a count, zeroifnull seems to make sense here.
which all make me think your SQL needs to look like:
SELECT
a.id,
a.login_id,
a.count,
case
WHEN a.count = 0 then 'Cat_A'
WHEN a.count between 1 and 10 then 'Cat_B'
WHEN a.count > 10 then 'Cat_C'
WHEN a.count IS NULL THEN 'Cat D'
END as Category,
ZEROIFNULL(b.count_of_orders) as count_of_orders
FROM (
SELECT
id,
login_id,
min(ord_count) AS count
FROM table_1 AS X
JOIN table_2 AS Y
ON X.id_col = Y.id_col
WHERE date = '2022-02-02'
AND login_id = 'True'
group by id,login_id
) as A
LEFT JOIN (
SELECT
x.id,
COUNT(X.ord_no) AS count_of_orders
FROM table_1 as X
WHERE X.date = '2022-02-02'
group by x.id
)as B
ON A.id=B.id
The A sub-select really should use the aliases you named X, Y so we know which tables id, login_id, ord_count, & date all come from.

Distinct columns after "ORDER BY" in SQL server 2014

I want to distinct columns after ORDER BY points.pPoint.
this is points table diagram:
I want something as following image on the right side but getting result as the left side:
and this is my code:
SELECT TOP(6) MedicalExpertise.meid
FROM physician INNER JOIN
MedicalExpertise ON physician.meid = MedicalExpertise.meid INNER JOIN
points ON physician.phId = points.phID
ORDER BY points.pPoint DESC
Perhaps something like this?
SELECT DISTINCT meid
FROM ( SELECT TOP ( 6 ) MedicalExpertise.meid
FROM physician
INNER JOIN MedicalExpertise ON physician.meid = MedicalExpertise.meid
INNER JOIN points ON physician.phId = points.phID
ORDER BY points.pPoint DESC ) d
ORDER BY 1 DESC;
Simply use distinct keyword,
Ex:
SELECT DISTINCT column1, column2, ...
FROM table_name;
Can you edit the original question what you really want? Does it have to be grouped by any column before getting distinct? Please update the question.

Using sub-queries and filter in WHERE clause while joining tables

SELECT DISTINCT(t1.Ticker),t2.SecurityID,t2.ClosePrice,t2.QuoteDateTime FROM [Hub].[SecurityMaster].[SecurityMasterDetails] as t1
INNER JOIN [Hub].[SecurityMaster].[SecurityPrices] as t2
ON t2.SecurityID =t1.SecurityID
WHERE t2.QuoteDateTime IN (SELECT max(QuoteDateTime) FROM [Hub].[SecurityMaster].[SecurityPrices]) AND t1.SecurityTypeName = 'REIT'
I get an output with no data. The subquery doesn't run along with the other filter in the WHERE clause. I am not sure what I am doing wrong. Can somebody please help!
If you are trying to get the lastest row from SecurityPrices for each Ticker, one option is to use cross apply():
select --distinct /* distinct not needed if `Ticker` is unique on `smd`
smd.Ticker
, sp.SecurityID
, sp.ClosePrice
, sp.QuoteDateTime
from [Hub].[SecurityMaster].[SecurityMasterDetails] as smd
cross apply (
select top 1
i.SecurityID
, i.ClosePrice
, i.QuoteDateTime
from [Hub].[SecurityMaster].[SecurityPrices] i
where i.SecurityID = smd.SecurityID
order by i.QuoteDateTime desc
) as sp
where SecurityTypeName = 'REIT' /* which table does this column belong to? */
I think your query would be
SELECT DISTINCT TOP 1 WITH TIES
t1.Ticker,
t2.SecurityID,
t2.ClosePrice,
t2.QuoteDateTime
FROM [Hub].[SecurityMaster].[SecurityMasterDetails] as t1
INNER JOIN [Hub].[SecurityMaster].[SecurityPrices] as t2 ON t2.SecurityID =t1.SecurityID
WHERE SecurityTypeName = 'REIT'
ORDER BY t2.QuoteDateTime DESC
You aren't getting results because the max(QuoteDateTime) record doesn't have SecurityTypeName = 'REIT'. I think you want the max(QuoteDateTime) for this SecurityTypeName, so this can be done with an INNER JOIN.
SELECT DISTINCT
(t1.Ticker),
t2.SecurityID,
t2.ClosePrice,
t2.QuoteDateTime
FROM [Hub].[SecurityMaster].[SecurityMasterDetails] as t1
INNER JOIN [Hub].[SecurityMaster].[SecurityPrices] as t2
ON t2.SecurityID =t1.SecurityID
INNER JOIN
(SELECT max(QuoteDateTime) DT FROM [Hub].[SecurityMaster].[SecurityPrices]) P on P.DT = t2.QuoteDateTime
WHERE SecurityTypeName = 'REIT'
EDIT
Your data doesn't have what you think it does, I suspect. Here is how you can check...
--Find the SecurityID that matches the max date
SELECT
SecurityID ,
max(QuoteDateTime) DT
FROM [Hub].[SecurityMaster].[SecurityPrices]
GROUP BY SecurityID
--I'm betting this ID isn't in your SecurityMasterDetails where the Type is REIT
SELECT DISTINCT
SecurityID
FROM SecurityMasterDetails
WHERE SecurityTypeName = 'REIT'
Since the SecurityID returned in the first query isn't in the second query result set, you are going to get NULL results.

Hints are not allowed on recursive common table expression (CTE) references. Consider removing hint from recursive CTE reference 'CTE'

I have one Employee table here is table structure
Name varchar
GUID numeric
ParentGUID numeric
here is some sample data
NAME GUID ParentGUID
ABC 1 NULL
BCD 2 1
xyz 3 2
PQR 4 2
MRS 5 3
This table contains big hierarchy of Employee and manager.
I need to pick all the Employee coming under particular employee.
Ex. I need all the Employees coming under BCD, so result should be
xyz 3 2
PQR 4 2
here is my recursive query for that.
;WITH CTE (Name, GUID, ParentGUID)
AS
(
select distinct B.Name , B.GUID, B.ParentGUID
FROM
EMP B with (nolock)
union All
select a.Name , a.GUID, a.ParentGUID
FROM EMP a with (nolock)
inner join CTE C with (nolock) on a.ParentGUID = c.GUID
)
select *
FROM CTE B with (nolock) where B.Name in ('BCD')
But it's giving me error.
Msg 4150, Level 16, State 1, Line 1
Hints are not allowed on recursive common table expression (CTE) references. Consider removing hint from recursive CTE reference 'CTE'.
Can you anyone please help me to correct this query.
Your where B.Name in ('BCD') is what is filtering your result set to just the one row. Change it to the below and you should get the results you want:
;with cte (Name, GUID, ParentGUID)
as
(
select distinct B.Name
,B.GUID
,B.ParentGUID
from EMP B
where B.Name in ('BCD')
union All
select a.Name
,a.GUID
,a.ParentGUID
from EMP a
inner join CTE C
on a.ParentGUID = c.GUID
)
select *
from CTE
Completely removing the WITH (NOLOCK) only solve the error,
to use WITH (NOLOCK) in recursive CTE you just need to put it outside of WITH clause only
;with cte (Name, GUID, ParentGUID)
as
(
select distinct B.Name
,B.GUID
,B.ParentGUID
from EMP B
where B.Name in ('BCD')
union All
select a.Name
,a.GUID
,a.ParentGUID
from EMP a
inner join CTE C
on a.ParentGUID = c.GUID
)
select *
from cte WITH (NOLOCK);

How to do sql join to get back rows not in right table

The DB I'm working with has the following 2 tables:
tblGroup
GroupId
GroupName
OtherGroupField, etc.
tblParts
PartId
PartNumber
GroupId
Price
OtherPartField, etc.
In my query I'd like to get the GroupId(s) which aren't in tblParts for a specified PartNumber
You can use the NOT IN predicate like this:
SELECT *
FROM tblGroups
WHERE GroupId NOT IN
(
SELECT GroupId
FROM tblParts
WHERE PartNumber = 'Some number'
AND GroupId IS NOT NULL
)
I would use an EXISTS with a join, rather than IN for better performance.
SELECT *
FROM tblGroups G
WHERE 1=1
And Not Exists
(
SELECT 1
FROM tblParts P
WHERE 1=1
And G.GroupId = P.GroupId
And PartNumber = 'PartNumberGoesHere'
)
My preference is EXCEPT
select GroupId from tblGroup
except
select GroupId from tblParts where PartNumber = 22
Its a little cleaner syntactically than outer joins even with a single item, but you can also use it to compare whole rows, e.g. looking for all rows of {A,B,C} that exist in table1 but not table2:
select A, B, C from table1
except
select A, B, C from table2
try with this
SELECT *
FROM tblGroups G
LEFT OUTER JOIN tblParts P ON P.GroupId=G.GroupId
WHERE P.PartId IS NULL

Resources