SQL Server reference fields in derived table with unions - sql-server

I'm having a bit of an issue with some derived tables that I hope someone will be able to help with. What I've got is 2 derived tables inside a select statement that then uses a pivot to display the results horizontally rather than vertically.
What I've got so far is:
SELECT * FROM(
SELECT SUM(Value) AS TotDays, ClassId FROM MainTable GROUP BY ClassId)
Union All
SELECT SUM(NumDays) As TotDays, ClassId FROM (
SELECT CASE WHEN COUNT(SiteId) > 0 THEN 1 ELSE 0 END AS NumDays
FROM Table2 GROUP BY ClassId ) as SUB
) AS a
PIVOT (SUM(TotDays) FROM ClassId
IN ([12],[13],[14],[15]
What I'm trying to do is reference the individual columns rather than using SELECT *, but I don't know how to do it. I can make it work without if I drop everything from the union onwards, but when I put the union in it doesn't work and I have to use SELECT *.
Anyone got any ideas on what's going wrong?
Thanks
Alex

You have a couple of errors on your query. For example, your UNION ALL has sets with a different number of columns, and you have other syntax errors. Try this way:
SELECT [12],[13],[14],[15]
FROM ( SELECT SUM(Value) AS TotDays, ClassId
FROM MainTable
GROUP BY ClassId
UNION ALL
SELECT SUM(NumDays) As TotDays, ClassId
FROM ( SELECT CASE WHEN COUNT(SiteId) > 0 THEN 1 ELSE 0 END NumDays,
ClassId
FROM Table2
GROUP BY ClassId) as SUB
) AS a
PIVOT (SUM(TotDays) FROM ClassId IN ([12],[13],[14],[15])) AS PT

Related

SQL Server : Combining Query Results

I have a program that stores different types "Assets" for buildings which can be marked as being "Removed". I can create a query to count the number of assets by type and another to count the number of items identified as still present. But what I want to do is combine the two into 1 table.
Query 1
SELECT
theAssetOutletType, COUNT(id) AS TotalNoOfAssets
FROM
dbo.tblLEGAssets
WHERE
buildingID = 1
GROUP BY
theAssetOutletType
Query 2
SELECT
theAssetOutletType, COUNT(id) AS ItemsStillPresent
FROM
dbo.tblLEGAssets
WHERE
buildingID = 1 AND removed <> 0
GROUP BY
theAssetOutletType
Thank you in advance for any help
I would suggest conditional aggregation:
SELECT theAssetOutletType,
COUNT(*) as TotalNoOfAssets
SUM(CASE WHEN removed <> 0 THEN 1 ELSE 0 END) as ItemsStillPresent
FROM dbo.tblLEGAssets
WHERE buildingID = 1
GROUP BY theAssetOutletType;
This puts the values in separate columns on the same row -- which makes more sense to me than on separate rows.
Try Union:
SELECT theAssetOutletType, count(id) as TotalNoOfAssets FROM dbo.tblLEGAssets where buildingID=1 group by theAssetOutletType
UNION
SELECT theAssetOutletType, count(id) as ItemsStillPresent FROM dbo.tblLEGAssets where buildingID=1 and removed<> 0 group by theAssetOutletType
I've found a work around, by using a nested select - that I can use, but if it is still possible I'd love to know the answer:
SELECT theassetoutlettype,
noofitems,
noremoved,
noofitems - noremoved AS noLeft
FROM (SELECT theassetoutlettype,
Count(id) AS NoOfItems,
Sum(removed) AS noRemoved
FROM dbo.tbllegassets
WHERE buildingid = 1
GROUP BY theassetoutlettype) nested
I finally found a solution... took a lot of trial and error but it now works. Here is my Stored Procedure Solution, with the additional table "tblLEGAssetOutletTypes" which contains a single fields with a list of the 6 asset types. The following code will always return 6 rows with the project type, the total number of assets, the number of assets removed and the total remaining. Hope someone else who needs a similar problem resolving an use the code:
SELECT tblLEGAssetOutletTypes.assetOutletType, nested.NoOfItems, nested.noRemoved, NoOfItems-noRemoved AS noLeft
FROM (SELECT theAssetOutletType, COUNT(id) AS NoOfItems, SUM(removed) AS noRemoved
FROM tblLEGAssets
where buildingID=#buildingID
GROUP BY theAssetOutletType) AS nested
RIGHT JOIN tblLEGAssetOutletTypes ON nested.theAssetOutletType = tblLEGAssetOutletTypes.assetOutletType;

Issue while using 'case when ' in 'where' clause sql server

While using case when in where clause in sql query it's not working.
Problem :
I have two tables named TblEmployee and TblAssociate.Both tables contains common columns PeriodId, EmpId and AssociateId. My requirement is to fetch values from
TblEmployee with combination of EmpId and AssociateId from TblAssociate should be excluded.And the exclusion should be based on PeriodId condition.`
If(#PeriodID<50)
BEGIN
SELECT *
FROM TblEmployee
WHERE (EmpId+AssociateId) NOT IN (SELECT EmpId+AssociateId FROM TblAssociate)
END
ELSE
BEGIN
SELECT *
FROM TblEmployee
WHERE (EmpId) NOT IN (SELECT EmpId FROM TblAssociate)
END
The above code is working, but I need to avoid that IF-ELSE condition and I wish to use 'case when' in where clause.Please help
Try this:
SELECT *
FROM TblEmployee
WHERE (EmpId + CASE WHEN #PeriodID<50 THEN AssociateId ELSE 0 END) NOT IN
(SELECT EmpId + CASE WHEN #PeriodID<50 THEN AssociateId ELSE 0 END FROM TblAssociate)
You say your code is working but this is rather odd, since it doesn't make much sense to add together id values. In any case, the above statement produces a result that is equivalent to the code originally posted.
You could use AND-OR combination in the WHERE clause. Additionally, you should not be using + as it may lead to incorrect result. You can rewrite your query as:
SELECT e.*
FROM TblEmployee e
WHERE
(
#PeriodID < 50
AND NOT EXISTS(
SELECT 1
FROM TblAssociate a
WHERE
a.EmpId = e.EmpId
AND a.AssociateId = e.AssociateId
)
)
OR
(
#PeriodID >= 50
AND NOT EXISTS(
SELECT 1
FROM TblAssociate a
WHERE a.EmpId = e.EmpId
)
)
The addition of IDs do not guarantee uniqueness. For instance, if EmpId is 5 and AssociateId is 6, then EmpId + AssociateId = 11, while EmpId + AssociateId = 11 even if EmpId is 6 and AssociateId is 5. In the query below, I made sure that the subquery will stop searching when the first record is found and will return a single record, having the value of 1. We select the employee if and only if 1 is among the results. In the subquery we check the operand we are sure of first and then check if we are not in a period where AssociateId must be checked, or it matches.
select *
from TblEmployee
where 1 in (select top 1 1
from TblAssociate
where TblEmployee.EmpId = TblAssociate.EmpId and
(#PeriodID >= 50 or TblEmployee.AssociateId = TblAssociate.AssociateId))

select statement with "Group by" on specific columns but displaying other columns along with group by columns

I want to get all data based on group by of only encounter,medicationname
column data..
select encounter,medicationname,count(*) as freq,labdate,result
from Medications where (labdate between #admitdate and DATEDIFF(dd,24,#admitdate))
group by encounter,medicationname having count(*)>2
I have records like
encounter medicationname freq
8604261 ACC 3
Now based on this data ,I want to get
This is my desired output
encounter medicationname labtime result
8604261 ACC 2015-05-22 18
8604261 ACC 2015-07-23 23
8604261 ACC 2015-09-09 27
You can use COUNT() as a window function, something like this:
;With Counted as (
SELECT encounter,medicationname,labdate,result,
COUNT(*) OVER (PARTITION BY encounter,medicationname) as cnt
from Medications
where (labdate between #admitdate
and DATEDIFF(dd,24,#admitdate))
)
select encounter,medicationname,labdate,result
from Counted
where cnt > 2
I would note that I think DATEDIFF1 is probably wrong also but since I don't have your data, inputs and an actual spec, I've left it as is for now.
1DATEDIFF returns an int, but you're using it in a comparison against a column which is apparently a date. DATEADD would be the more probably desired function here, but as I say, I don't have full information to go on.
If I understand you question correctly what you need is this
;WITH CTE AS
(
select encounter,medicationname,count(*) as freq,labdate,result
from Medications where (labdate between #admitdate and DATEDIFF(dd,24,#admitdate))
group by encounter,medicationname having count(*) > 2
)
select encounter,medicationname,labdate,result
from Medications M
INNER JOIN CTE C
ON M.encounter = C.encounter
AND M.medicationname = C.medicationname
where (labdate between #admitdate and DATEDIFF(dd,24,#admitdate))
or better yet using COUNT()OVER()
;WITH CTE AS
(
SELECT encounter,medicationname,COUNT(*) OVER(PARTITION BY encounter,medicationname)as freq,labdate,result
FROM Medications
WHERE (labdate between #admitdate and DATEDIFF(dd,24,#admitdate))
)
SELECT * FROM CTE
WHERE freq > 2
select encounter,medicationname,count(*) as freq,labdate,result
from Medications
where (labdate between #admitdate and DATEDIFF(dd,24,#admitdate))
group by encounter,medicationname having count(*) > 2

T-SQL order by, based on other column value

I'm stuck with a query which should be pretty simple but, for reasons unknown, my brain is not playing ball here ...
Table:
id(int) | strategy (varchar) | value (whatever)
1 "ABC" whatevs
2 "ABC" yeah
3 "DEF" hello
4 "DEF" kitty
5 "QQQ" hurrr
The query should select ALL rows grouped on strategy but only one row per strategy - the one with the higest id.
In the case above, it should return rows with id 2, 4 and 5
SELECT id, strategy , value
FROM (
SELECT id, strategy , value
,ROW_NUMBER() OVER (PARTITION BY strategy ORDER BY ID DESC) rn
FROM Table_Name
) Sub
WHERE rn = 1
Working SQL FIDDLE
You can use window function to get the solution you want. Fiddle here
with cte as
(
select
rank()over(partition by strategy order by id desc) as rnk,
id, strategy, value from myT
)
select id, strategy, value from
cte where rnk = 1;
Try this:
SELECT T2.id,T1.strategy,T1.value
FROM TableName T1
INNER JOIN
(SELECT MAX(id) as id,strategy
FROM TableName
GROUP BY strategy) T2
ON T1.id=T2.id
Result:
ID STRATEGY VALUE
2 ABC yeah
4 DEF kitty
5 QQQ hurrr
See result in SQL Fiddle.
SELECT id, strategy , value
FROM (
SELECT id, strategy , value
,MAX(id) OVER (PARTITION BY strategy) MaxId
FROM YourTable
) Sub
WHERE id=MaxId
You may try this one as well:
SELECT id, strategy, value FROM TableName WHERE id IN (
SELECT MAX(id) FROM TableName GROUP BY strategy
)
Bit depends on your data, you might get results faster with it as it does not do sorting, but by the other hand it uses IN, which can slow you down if there is many 'strategies'

SQL Server - Insert into with select and union - duplicates being inserted

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.

Resources