I have table like this:
Castomer_ID Survay_result
John Good
Ben Bad
John Bad
John Good
Ben Bad
Ben Good
I want to create query witch return this result:
Castomer_ID Good Bad
John 2 1
Ben 1 2
I tried use subquery for this:
SELECT Castomer_ID AS temp_Castomer_ID,
(SELECT COUNT(Survay_result)
FROM test1
WHERE
temp_Castomer_ID = Castomer_ID
AND
template_id = Good) AS 'Good',
(SELECT COUNT(Survay_result)
FROM test1
WHERE
temp_Castomer_ID = Castomer_ID
AND
template_id = Bad) AS 'Bad',
FROM test
GROUP BY
Castomer_ID
and I see next error massage:
Msg 207, Level 16, State 1, Line 5
Invalid column name 'temp_Castomer_ID'.
Msg 207, Level 16, State 1, Line 11
Invalid column name 'temp_Castomer_ID'.
What am I doing wrong? Help please.
I would suggest you actually just use conditional aggregation with a JOIN:
SELECT t.Castomer_ID AS temp_Castomer_ID, --Customer doesn't have an a
COUNT(CASE t1.Survay_result WHEN 'Good' THEN 1 END) AS Good, --Don't use literal strings for aliases. Survey doesn't have an a
COUNT(CASE t1.Survay_result WHEN 'Bad' THEN 1 END) AS Bad --Don't use literal strings for aliases. Survey doesn't have an a
FROM dbo.test t
LEFT JOIN dbo.test1 t1 ON t.Castomer_ID = t1.Castomer_ID
GROUP BY t.Castomer_ID;
Use table aliases and qualify the columns to resolve name conflicts.
SELECT t.castomer_id,
(SELECT count(*)
FROM test AS tg
WHERE tg.castomer_id = t.castomer_id
AND tg.survay_result = 'Good') AS good,
(SELECT count(*)
FROM test AS tb
WHERE tb.castomer_id = t.castomer_id
AND tb.survay_result = 'Bad') AS bad
FROM test AS t
GROUP BY t.castomer_id;
Also:
The objects in your query and the given sample don't fully match. You'll sort this out yourself.
Don't get used to use single quotes for identifiers such as column aliases. Yes, sadly SQL Server accepts that in some places, but in SQL single quotes are usually for string (or date...) literals. Should you ever use another DBMS (or future SQL Server versions become more sane about this) you'll likely get an error. Use square brackets for identifiers that have special characters in them or are case sensitive or preferably don't use special characters and case sensitive identifiers at all. Identifiers don't need to be "pretty", "pretty" headers and such are a job for the presentation layer.
But you have to quote string literals.
Instead of the subqueries you could also use conditional aggregation. This might perform better, you need to test this.
SELECT t.castomer_id,
count(CASE
WHEN t.survay_result = 'Good' THEN
1
END) AS good,
count(CASE
WHEN t.survay_result = 'Bad' THEN
1
END) AS bad
FROM test AS t
GROUP BY t.castomer_id;
create table #temp1 (Castomer_ID varchar(5),Survay_result varchar(5))
insert into #temp1 values
('John','Good'),('Ben',' Bad'),('John','Bad'),('John','Good'),('Ben',' Bad'),('Ben',' Good')
select Castomer_ID,sum(good)[Good],sum(bad)[Bad] from(
select Castomer_ID, case when Survay_result like '%Good%' then count(1) ELSE 0 end [Good],case when Survay_result like '%Bad%' then count(1) ELSE 0 end [Bad] from #temp1
group by Castomer_ID,Survay_result)h
group by Castomer_ID
Related
I'm creating an SSRS report and during writing the query I look up the code logic for the data that needs to be retrieved in query. There is lots of usage of !String.IsNullOrEmpty method so I want to know what is the shortest and best way to do the equivalent check in SQL server?
WHERE t.Name IS NOT NULL OR T.Name != ''
or....
WHERE LEN(t.Name) > 0
which one is correct? Or is there any other alternative?
There is no built-in equivalent of IsNullOrEmpty() in T-SQL, largely due to the fact that trailing blanks are ignored when comparing strings. One of your options:
where len(t.Name) > 0
would be enough as len() ignores trailing spaces too. Unfortunately it can make the expression non-SARGable, and any index on the Name column might not be used. However, this one should do the trick:
where t.Name > ''
P.S. For the sake of completeness, the datalength() function takes all characters into account; keep in mind however that it returns the number of bytes, not characters, so for any nvarchar value the result will be at least double of what you might expect (and with supplementary characters / surrogate pairs the number should be even higher, if my memory serves).
If the desired result is the simplest possible one-liner then:
WHERE NULLIF(Name, '') IS NOT NULL
However, from a performance point of view following alternative is SARGable, therefore indexes potentially can be used to spot and filter out records with such values
WHERE Name IS NOT NULL AND Name != ''
An example:
;WITH cte AS (
SELECT 1 AS ID, '' AS Name UNION ALL
SELECT 2, ' ' UNION ALL
SELECT 3, NULL UNION ALL
SELECT 4, 'abc'
)
SELECT * FROM cte
WHERE Name IS NOT NULL AND Name != ''
Results to:
ID Name
---------
4 abc
Yes, you can use WHERE LEN(t.Name)>0.
You can also verify as below:
-- Count the Total number of records
SELECT COUNT(1) FROM tblName as t
-- Count the Total number of 'NULL' or 'Blank' records
SELECT COUNT(1) FROM tblName as t WHERE ISNULL(t.Name,'')= ''
-- Count the Total number of 'NOT NULL' records
SELECT COUNT(1) FROM tblName as t WHERE LEN(t.Name)>0
Thanks.
I am creating a query of all people who were screened for smoking status and need a count of unique patients. I am pulling from an encounter table, so the patient could have been asked multiple times. In my case when statement I would like to limit the "Then..." result to something like "Then count distinct patients" but it is giving me an error about aggregates not being allowed within an aggregate. If I remove it, it will then not produce a total as I wish and it's telling me I need it in the group by clause, which I do not want. limit is not an option in sql-server to the best of my knowledge
,count(case when soc.tobacco_user_c in (1, 2, 4, 5) and dmw.SMOKING_CESS_CNSL_YN ='y' then enc.PAT_ID **Here is where I want a unique count of patients** end) Compliant
You can combine DISTINCT with a CASE expression.
Example
SELECT
COUNT(DISTINCT CASE WHEN tobacco = 1 THEN PAT_ID ELSE NULL END)
...
;
I've abbreviated your example to make it easier to read. NULLs will not be included in the final count, so there is no need to worry about off by one errors.
case when soc.tobacco_user_c in (1, 2, 4, 5) and dmw.SMOKING_CESS_CNSL_YN ='y' then COUNT(DISTINCT enc.PAT_ID) ELSE 0 end Compliant
I ended up creating two subqueries and then doing a select count distinct on each of the max columns in those queries to limit the results to one
I have a table Emp like this for example.
----------------------
eName | eId
----------------------
Anusha 1
Sunny 2
Say i am looking for an entry whose id is 3.I want to write a query which finds the row and displays it.But if it doesnt find it it is expected to display a default row (temp,999)
select case
when (total != 0) then (select eName from Emp where eId = 3)
when (total == 0) then "temp"
end as eName,
case
when (total != 0) then (select eId from Emp where eId = 3)
when (total == 0) then 999
end as eId
from Emp,(select count(*) as total from Emp where eId = 3);
Using this query that i wrote it gives me two rows as a result.
temp 999
temp 999
I assume it is because of
(select count(*) as total from Emp where eId = 3) this query in the from list of the query.
I tried using the distinct clause and it gives me just a single row. But i am a little doubtful if i am messing the query and only trying to probably employ a hack to do it.Please suggest if there is a better way to do this or if i am wrong.
I'll get to how to do this right, but first let me give you a long answer to maybe help you with your understanding of SQL. What's happening to use is this:
Your select clause does not affect the number of records you get. So to understand what's happenning, let's simpify the query a little. Let's change it to,
select * from emp, (select count(*) as total from emp where eid=3)
I'm not sure what you think the comma after "emp" does here, but SQL see this as an old-style join on two tables: emp and the temporary table created by the select count(*), etc. There is no WHERE clause, so this is a cross join, but the second table only has one record anyway, so that part doesn't matter. But the fact that there is no WHERE clause means that you will get every record in emp, joined to the count. So the output of this query is:
ename eid count(*)
Anusha 1 0
Sunny 2 0
If you had 100 records, you would get 100 results.
Frankly there is no really clean way to do what you want in SQL. It's the sort of thing that's cleaner to do in code: do a plain "select ... where eid=3", and if you get no records, fill in the default at run-time.
But assuming that you need to do it in SQL for some reason, I think the simplest way would be:
select eid, ename from emp where eid=3
union
select 999 as eid, 'temp' as ename
where not exists (select 1 from emp where eid=3)
In some versions of SQL you need to give a dummy table name on the second select, like Oracle requires you to say "from dual".
When I execute my "select union select", I get the correct number or rows (156)
Executed independently, select #1 returns 65 rows and select #2 returns 138 rows.
When I use this "select union select" with an Insert into, I get 203 rows (65+138) with duplicates.
I would like to know if it is my code structure that is causing this issue ?
INSERT INTO dpapm_MediaObjectValidation (mediaobject_id, username, checked_date, expiration_date, notified)
(SELECT FKMediaObjectId, CreatedBy,#checkdate,dateadd(ww,2,#checkdate),0
FROM dbo.gs_MediaObjectMetadata
LEFT JOIN gs_MediaObject mo
ON gs_MediaObjectMetadata.FKMediaObjectId = mo.MediaObjectId
WHERE UPPER([Description]) IN ('CAPTION','TITLE','AUTHOR','DATE PHOTO TAKEN','KEYWORDS')
AND FKMediaObjectId >=
(SELECT TOP 1 MediaObjectId
FROM dbo.gs_MediaObject
WHERE DateAdded > #lastcheck
ORDER BY MediaObjectId)
GROUP BY FKMediaObjectId, CreatedBy
HAVING count(*) < 5
UNION
SELECT FKMediaObjectId, CreatedBy,getdate(),dateadd(ww,2,getdate()),0
FROM gs_MediaObjectMetadata yt
LEFT JOIN gs_MediaObject mo
ON yt.FKMediaObjectId = mo.MediaObjectId
WHERE UPPER([Description]) = 'KEYWORDS'
AND FKMediaObjectId >=
(SELECT TOP 1 MediaObjectId
FROM dbo.gs_MediaObject
WHERE DateAdded > #lastcheck
ORDER BY MediaObjectId)
AND NOT EXISTS
(
SELECT *
FROM dbo.fnSplit(Replace(yt.Value, '''', ''''''), ',') split
WHERE split.item in (SELECT KeywordEn FROM gs_Keywords) or split.item in (SELECT KeywordFr FROM gs_Keywords)
)
)
I would appreciate any clues into resolving this problem ...
Thank you !
The UNION keyword should only return distinct records between the two queries. However, if I recall correctly, this is only true if the datatypes are the same. The date variables might be throwing that off. Depending on the collation type, whitespace might be handled differently as well. You might want to do a SELECT DISTINCT on the dpapm_MediaObjectValidation table after doing your insert, but be sure to trim whitespace from both sides in your comparison. Another approach is to do your first insert, then on your second insert, forgo the UNION altogether and do a manual EXISTS check to see if the items to be inserted already exist.
I would like to think of myself as a decent SQL Server programmer but this query always stumps me. I'm sure there is a much better way to query this.
SELECT DISTINCT
database_name,
CASE
WHEN ((SELECT MAX(1) FROM dbo.column_list WHERE column_list IS NOT NULL and database_id = d.database_id) = 1
OR (SELECT MAX(1) FROM dbo.table_list WHERE table_list IS NOT NULL and database_id = d.database_id) = 1)
THEN 1
ELSE 0
END AS 'has_definition'
FROM dbo.database_list d;
I have 3 tables database_list, table_list and a column_list. I'm trying to query for a given database in the database list to see if any of the underlying table or column has definitions in them. This query works now and returns a unique list of databases and a has_definition but this seems wrong to me.
Any thoughts would be helpful.
I'd change it to use EXISTS(SELECT * ....) OR EXISTS(SELECT * ....) rather than the SELECT(MAX) business but apart from that looks OK to me.
SQL Server will probably show a plan with a concatenation operator that short circuits the OR as per my tests here. I also found that it would test the cheaper one to evaluate first in this case irrespective of order of the clauses.
You might try this version instead:
SELECT database_name,
CASE WHEN EXISTS
(
SELECT 1 FROM dbo.column_list
WHERE column_list IS NOT NULL and database_id = d.database_id
UNION ALL
SELECT 1 FROM dbo.table_list
WHERE table_list IS NOT NULL and database_id = d.database_id
)
THEN 1
ELSE 0
END AS [has_definition]
FROM dbo.database_list AS d;
And maybe compare it to this:
SELECT database_name,
CASE WHEN EXISTS
(
SELECT 1 FROM dbo.column_list
WHERE column_list IS NOT NULL and database_id = d.database_id
)
OR EXISTS
(
SELECT 1 FROM dbo.table_list
WHERE table_list IS NOT NULL and database_id = d.database_id
)
THEN 1
ELSE 0
END AS [has_definition]
FROM dbo.database_list AS d;
I don't think you'll need the distinct unless you really have duplicates in dbo.database_list.
Also please don't use 'single quotes' for aliases - this syntax is deprecated, you should use [square brackets] instead.