How to construct a SQL sub query in SQL Server 2008? - sql

I have requirement to extract total number of rows from a table - ci_periodicBillings only for clients where they have rows from a particular date range from another table - ci_invoiceHeaders. I am using MS SQL Server 2008, connecting via ODBC.
I have created a subquery which works but only if the total number of rows from ci_periodicBillings is 1. I'm finding if there is more than 1 result from ci_periodicBillings, it's multiplying the rows found by the number of rows meeting the criteria from ci_invoiceHeaders.
I only want to show only the rows from ci_periodicBillings without any multiplication if the criteria is met in ci_invoiceHeaders. I'm sure there is an easy solution to this but I can't see the wood from the trees at the moment.
There are a few other tables used for listing purposes only (i.e. facilities/clients etc)
SQL is here:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT
b.name,
b.forename,
b.surname,
a.client,
cast(a.BILLSTART as DATE) as BILLSTART,
cast(a.ENDBILL as DATE) as ENDBILL,
a.RATE
FROM ci_periodicBillings as a
inner join
(select f.name,
c.surname,c.forename,ih.client,ih.invoiceDate
FROM ci_invoiceHeaders ih
LEFT JOIN ci_invoiceDetails id ON ih.invoiceNo = id.id
INNER JOIn cs_clients c ON ih.client = c.guid
INNER JOIN cs_facilities f ON c.facility = f.guid
group by f.name, c.surname,
c.forename, ih.client, ih.invoiceDate)
as b
on a.client = b.client
WHERE b.invoiceDate between '2017-08-01' and '2018-01-31'
order by a.client
Any ideas please?

Try this:
SELECT b.name, b.forename, b.surname, a.client,
cast(a.BILLSTART AS DATE) AS BILLSTART,
cast(a.ENDBILL AS DATE) AS ENDBILL, a.RATE
FROM ci_periodicBillings AS a inner join
(SELECT f.name, c.surname,c.forename,ih.client,DATE(ih.invoiceDate) invoiceDate
FROM ci_invoiceHeaders ih
LEFT JOIN ci_invoiceDetails id ON ih.invoiceNo = id.id
INNER JOIn cs_clients c ON ih.client = c.guid
INNER JOIN cs_facilities f ON c.facility = f.guid
WHERE ih.invoiceDate BETWEEN '2017-08-01' AND '2018-01-31'
GROUP BY f.name, c.surname,c.forename,ih.client,DATE(ih.invoiceDate)) AS b
ON a.client = b.client
ORDER BY a.client;

Related

Replace correlated subquery with CTE and JOIN

I am trying to rewrite a query which has a correlated subquery, the idea is to replace it with a CTE and join it later.
I have three tables, tbl_transaction, tbl_beneficiaries and tbl_reg_countries. The current (in short) SQL looks like the following.
SELECT
t.USER_ID,
t.TRANSACTION
FROM tbl_transactions t
JOIN tbl_beneficiaries b ON b.ID = t.USER_ID
WHERE b.COUNTRY NOT IN (
SELECT rc.country
FROM tbl_reg_countries rc
WHERE rc.id = t.USER.ID)
My goal is to query only those transactions for each user where the transaction happens outside of the registered countries. So a user may registered X,Y,Z country but had business with Q. In that case only Q should be returned. How could this be replaced with a CTE/JOIN?
I assume both tbl_beneficiaries.COUNTRY and tbl_reg_countries.COUNTRY are not nullable. You can use a LEFT JOIN with NULL test to detect never matching rows
SELECT
t.USER_ID,
t.TRANSACTION
FROM tbl_transactions t
JOIN tbl_beneficiaries b ON b.ID = t.USER_ID
LEFT JOIN tbl_reg_countries rc ON rc.id = t.USER_ID AND b.COUNTRY = rc.country
WHERE rc.country IS NULL
I would try rewriting query with "with"
Like this:
With a As
(Select
Distinct rc.country
From tbl_reg_countries rc
Inner Join tbl_transactions t on rc.id = t.USER.ID
)
Select
t.USER_ID,
t.TRANSACTION
From tbl_transactions t
Inner Join tbl_beneficiaries b On b.ID = t.USER_ID
Where b.COUNTRY Not In (select * from a)

How to create distinct count from queries with several tables

I am trying to create one single query that will give me a distinct count for both the ActivityID and the CommentID. My query in MS Access looks like this:
SELECT
tbl_Category.Category, Count(tbl_Activity.ActivityID) AS CountOfActivityID,
Count(tbl_Comments.CommentID) AS CountOfCommentID
FROM tbl_Category LEFT JOIN
(tbl_Activity LEFT JOIN tbl_Comments ON
tbl_Activity.ActivityID = tbl_Comments.ActivityID) ON
tbl_Category.CategoryID = tbl_Activity.CategoryID
WHERE
(((tbl_Activity.UnitID)=5) AND ((tbl_Comments.PeriodID)=1))
GROUP BY
tbl_Category.Category;
I know the answer must somehow include SELECT DISTINCT but am not able to get it to work. Do I need to create multiple subqueries?
This is really painful in MS Access. I think the following does what you want to do:
SELECT ac.Category, ac.num_activities, aco.num_comments
FROM (SELECT ca.category, COUNT(*) as num_activities
FROM (SELECT DISTINCT c.Category, a.ActivityID
FROM (tbl_Category as c INNER JOIN
tbl_Activity as a
ON c.CategoryID = a.CategoryID
) INNER JOIN
tbl_Comments as co
ON a.ActivityID = co.ActivityID
WHERE a.UnitID = 5 AND co.PeriodID = 1
) as caa
GROUP BY ca.category
) as ca LEFT JOIN
(SELECT c.Category, COUNT(*) as num_comments
FROM (SELECT DISTINCT c.Category, co.CommentId
FROM (tbl_Category as c INNER JOIN
tbl_Activity as a
ON c.CategoryID = a.CategoryID
) INNER JOIN
tbl_Comments as co
ON a.ActivityID = co.ActivityID
WHERE a.UnitID = 5 AND co.PeriodID = 1
) as aco
GROUP BY c.Category
) as aco
ON aco.CommentId = ac.CommentId
Note that your LEFT JOINs are superfluous because the WHERE clause turns them into INNER JOINs. This adjusts the logic for that purpose. The filtering is also very tricky, because it uses both tables, requiring that both subqueries have both JOINs.
You can use DISTINCT:
SELECT
tbl_Category.Category, Count(DISTINCT tbl_Activity.ActivityID) AS CountOfActivityID,
Count(DISTINCT tbl_Comments.CommentID) AS CountOfCommentID
FROM tbl_Category LEFT JOIN
(tbl_Activity LEFT JOIN tbl_Comments ON
tbl_Activity.ActivityID = tbl_Comments.ActivityID) ON
tbl_Category.CategoryID = tbl_Activity.CategoryID
WHERE
(((tbl_Activity.UnitID)=5) AND ((tbl_Comments.PeriodID)=1))
GROUP BY
tbl_Category.Category;

SQL Join two query

I need some help combining these two queries so I can get this in one single view.
Query 1
select t.*, n.caption, n.description
from (
select NodeID,
count(distinct cpuindex) as number_of_cpu,
case
When count(distinct cpuindex) < 8 THEN 1
Else count(distinct cpuindex)/8
End AS number_of_cores
from CPUMultiLoad_Detail (nolock) where nodeid in (select nodeid from nodesdata)
group by NodeID
) as t
inner join NodesData as n (nolock) on n.nodeid = t.nodeid
where n.description NOT Like '%Windows%'
order by n.description
Query 2
SELECT D.Environment, B.Name, C.Caption, A.ComponentStatisticData, A.ErrorMessage
FROM [APM_CurrentStatistics] A, APM_Application B, NodesData C
join NodesCustomProperties D on D.NodeID= C.NodeID
WHERE
A.ApplicationID=B.ID AND
A.NodeID=C.NodeID AND
B.Name IN ('Oracle Database Licensing')
I want to join first query and second query so I have CPU Information and Licensing Information in same table. How do I join both query? We can use common key Nodes.NodeID to join and not sure how. Any help will be greatly appreciated.
Consider joining the inner aggregate subquery of first query which holds distinct NodeID to the second query using a CTE. Additionally, use explicit JOIN(current standard in SQL) and heed bad habits to kick : using table aliases like (a, b, c) and use more informative table aliases.
WITH agg AS
(
select NodeID,
count(distinct cpuindex) as number_of_cpu,
case
when count(distinct cpuindex) < 8 THEN 1
else count(distinct cpuindex) / 8
end AS number_of_cores
from CPUMultiLoad_Detail
where nodeid in (select nodeid from nodesdata)
group by NodeID
)
SELECT cp.Environment, app.Name, n.Caption,
cs.ComponentStatisticData, cs.ErrorMessage,
agg.NodeID, agg.number_of_cpu, agg.number_of_cores, n.description
FROM APM_CurrentStatistics cs
INNER JOIN APM_Application app
ON cs.ApplicationID = app.ID
AND app.Name IN ('Oracle Database Licensing')
INNER JOIN NodesData n
ON cs.NodeID = n.NodeID
AND n.description NOT LIKE '%Windows%'
INNER JOIN NodesCustomProperties cp
ON cp.NodeID = n.NodeID
INNER JOIN agg
ON cs.NodeID = agg.NodeID
ORDER BY n.description

Putting the results of a query into new table in SQL Server

I want to insert query results into new table is there any way I can make changes in code so that it gets stored in a table.
My query:
SELECT DISTINCT TOP 5 a.DocEntry
,b.TrgetEntry
,b.itemcode
,a.DocNum AS 'Order No.'
,a.CardCode
,a.CardName
,b.DocDate AS [Delivery No.]
,c.targettype AS 'Ctargettype'
,c.trgetentry AS 'Ctargetentry'
,c.itemcode AS 'c-itemcode'
,c.docentry AS 'Cdocentry' a.CancelDate
,a.Project
,a.DocStatus
,b.ObjType
,a.ObjType
FROM ORDR a
INNER JOIN rdr1 b ON a.DocEntry = b.DocEntry
LEFT JOIN dln1 c ON c.TrgetEntry = b.DocEntry
AND b.itemcode = c.ItemCode order by c.itemcode;
You can do it as it will create a new table and insert the records into that table. If you have already created table then you can give name and individual columns also for both insertion and selection.
SELECT *
INTO YourTableName
FROM (
SELECT DISTINCT TOP 5 a.DocEntry
,b.TrgetEntry
,b.itemcode
,a.DocNum AS 'Order No.'
,a.CardCode
,a.CardName
,b.DocDate AS [Delivery No.]
,c.targettype AS 'Ctargettype'
,c.trgetentry AS 'Ctargetentry'
,c.itemcode AS 'c-itemcode'
,c.docentry AS 'Cdocentry' a.CancelDate
,a.Project
,a.DocStatus
,b.ObjType
,a.ObjType
FROM ORDR a
INNER JOIN rdr1 b ON a.DocEntry = b.DocEntry
LEFT JOIN dln1 c ON c.TrgetEntry = b.DocEntry
AND b.itemcode = c.ItemCode
)
a
For using order by clause you can try something like this.
SELECT DISTINCT
Insured_Customers.FirstName, Insured_Customers.LastName,
Insured_Customers.YearlyIncome, Insured_Customers.MaritalStatus
INTO Fast_Customers from Insured_Customers INNER JOIN
(
SELECT * FROM CarSensor_Data where Speed > 35
) AS SensorD
ON Insured_Customers.CustomerKey = SensorD.CustomerKey
ORDER BY YearlyIncome;
You can learn in detail about INTO Clause Here
This looks like SQL Server code. In that database, you add into after the select clause:
Select distinct top 5 o.DocEntry, r.TrgetEntry, r.itemcode, o.DocNum as order_num, o.CardCode,
o.CardName, r.DocDate as delivery_num,
d.targettype as Ctargettype, d.trgetentry as Ctargetentry,
d.itemcode as c_itemcode, d.docentry as Cdocentry
a.CancelDate,a.Project, a.DocStatus,b.ObjType,a.ObjType
into <new table>
from ORDR o inner join
rdr1 r
On o.DocEntry = r.DocEntry left join
dln1 d
on d.TrgetEntry = r.DocEntry and
d.itemcode = r.ItemCode;
Note that I changed the table aliases so they are meaningful. Arbitrary letters are very hard to follow. Table abbreviations are more useful.
I also changed the column aliases so they do not need to be escaped. Do not make troublesome aliases!

Column 'Pay_records.ActualTime' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause

I am trying to get the TimeCodeTotal per TimeCode. SQL Server doesn't like my current setup and wants me to add PR.Actual time to the GROUP BY.
This is not a solution because adding PR.ActualTime to the GROUP BY changes the results to incorrect values;
I only want to GROUP BY the values provided in the query below.
Is there another way to seperate by timecode without using the OVER (BY PARTITION) clause?
SELECT YEAR(PR.date) AS TimeYear
,SUBSTRING(DATENAME(MONTH,DATEADD(MONTH,MONTH(PR.Date),0)- 1),1,3) AS TimeMonth
,TC.TimeCode
,SUM(PR.ActualTime) OVER (PARTITION BY TC.TimeCode) AS TimeCodeTotal
,SUM(PR.ActualTime) AS MonthTotal
FROM Pay_records PR
INNER JOIN Employee E ON E.employee_no = PR.employee_no
INNER JOIN Dept_Names D ON D.Dept = E.department
INNER JOIN DepartmentTimeCategory DTC ON DTC.Dept = D.Dept
INNER JOIN TimeCategory TMC ON TMC.ID = DTC.TimeCategoryID
INNER JOIN TimeCode TC ON TC.TimeCodeID = TMC.TimeCodeID
WHERE PR.DATE BETWEEN '01/01/2015' AND '02/17/16'
AND D.Dept IN ('02Z103')
AND E.Billable IN (1,0)
GROUP BY YEAR(PR.date), MONTH(PR.date), TC.TimeCode
ORDER BY YEAR(PR.date), MONTH(PR.date)
I think an easy fix is to remove SUM(PR.ActualTime) OVER (PARTITION BY TC.TimeCode) AS TimeCodeTotal from the query, wrap the whole thing in a CTE, and then select everything from the CTE and add a SUM(TimeCodeTotal).
;WITH CTE AS (
SELECT YEAR(PR.date) AS TimeYear
,SUBSTRING(DATENAME(MONTH,DATEADD(MONTH,MONTH(PR.Date),0)- 1),1,3) AS TimeMonth
,TC.TimeCode
,SUM(PR.ActualTime) AS TimeCodeSubTotal
FROM Pay_records PR
INNER JOIN Employee E ON E.employee_no = PR.employee_no
INNER JOIN Dept_Names D ON D.Dept = E.department
INNER JOIN DepartmentTimeCategory DTC ON DTC.Dept = D.Dept
INNER JOIN TimeCategory TMC ON TMC.ID = DTC.TimeCategoryID
INNER JOIN TimeCode TC ON TC.TimeCodeID = TMC.TimeCodeID
WHERE PR.DATE BETWEEN '01/01/2015' AND '02/17/16'
AND D.Dept IN ('02Z103')
AND E.Billable IN (1,0)
GROUP BY YEAR(PR.date)
,MONTH(PR.date)
,TC.TimeCode
)
SELECT TimeYear
, TimeMonth
, TimeCode
, TimeCodeSubTotal
, Sum(TimeCodeSubTotal)
FROM CTE
GROUP BY TimeYear
, TimeMonth
, TimeCode
, TimeCodeSubTotal
ORDER BY YEAR(PR.date)
, MONTH(PR.date)