LOOP and COUNT in a SELECT statement SQL SERVER - sql

I have a view with these columns
and i want to extract this data
i am doing it in SQL Server manually in several steps and i want to automate it so that i can run a select statement directly from a macro and save it in excel file.
Here is what i am doing
select distinct
CASE
WHEN Userid ='jsolar' THEN 'Jack Solar'
WHEN Userid ='jkrcmarikova' THEN 'Jana Krcmarikova'
WHEN Userid ='lfialova' THEN 'lucia fialova'
WHEN Userid ='zsnopkova' THEN 'zuzana snopkova'
END AS [User Name]
, Region
from [SC].[vw_X86_Orders_By_UserID_GAMMA]
order by Region, [User Name]
Then i copy the result in excel file and run other queries for each user
SELECT Count ( DISTINCT [Order Number])
FROM [SC].[vw_X86_Orders_By_UserID_GAMMA]
where Userid LIKE 'jsolar'
AND Region LIKE 'CENTRAL'
and [Order Entry Date] = '2016-10-27'
i save this result in number of distinct order number. Then i run this query
SELECT Count ( DISTINCT CONCAT ([Order Number], [Line No]))
FROM [SC].[vw_X86_Orders_By_UserID_GAMMA]
where Userid LIKE 'jsolar'
AND Region LIKE 'CENTRAL'
and [Order Entry Date] = '2016-10-27'
I save this result in number of distinct order number concatenated with line no. And i repeat the same for each user
At the end it should be something like this in Excel
Is there a way how to do this in one select statement to loop each user and count for all users at the same time ? Thank you very much.

I really don't think you need a "LOOP". SQL operates best in set based operations returning recordsets with many rows. So we need to treat [SC].[vw_X86_Orders_By_UserID_GAMMA] as an entire set and simply update the case statement to translate all users names (assuming you have to have them, or you could do a vlookup on the userID in excel after the fact)
I think what you're really after is the count(distinct column) in combination with a group by on userID and region.
Based on comments I think you would need to amend the case statements in the select and group by to contain the translation for all the users.
I think there's too many unknowns to provide a 100% correct response but here's a shot across the bow..
SELECT CASE WHEN Userid ='jsolar' THEN 'Jack Solar'
WHEN Userid ='jkrcmarikova' THEN 'Jana Krcmarikova'
WHEN Userid ='******' THEN '**** *******'
WHEN Userid ='****' THEN '***** ****' END AS [User Name]
, Region
, count(distinct [Order Number]) as cntDistinctOrders
, count(Distinct concat([order Number], [Line No]) as cntDistinctOrderLines
FROM [SC].[vw_X86_Orders_By_UserID_GAMMA]
WHERE [Order Entry Date] = '2016-10-27'
-- and Region = 'CENTRAL' don't think you need this the group by handles same names in different regions keeping them distinct and counts seperate.
GROUP BY CASE WHEN Userid ='jsolar' THEN 'Jack Solar'
WHEN Userid ='jkrcmarikova' THEN 'Jana Krcmarikova'
WHEN Userid ='******' THEN '**** *******'
WHEN Userid ='****' THEN '***** ****'
END
, Region
ORDER BY [User Name], Region
To put this in plain English...
You want all the usernames and regions for each user in the [SC].[vw_X86_Orders_By_UserID_GAMMA] schema.table showing the distinct count of orders and order lines for a specific date.
If you can use a Vlookup in excel for the names you could getaway without the case statements and all that extra code..
SELECT UserId [User Name]
, Region
, count(distinct [Order Number]) as cntDistinctOrders
, count(Distinct concat([order Number], [Line No]) as cntDistinctOrderLines
FROM [SC].[vw_X86_Orders_By_UserID_GAMMA]
WHERE [Order Entry Date] = '2016-10-27'
-- and Region = 'CENTRAL' don't think you need this the group by handles same names in different regions keeping them distinct and counts seperate.
GROUP BY CASE UserID
, Region
ORDER BY [User Name], Region
--note because the group by executes before the select statement, we have to group by the USERID and not the alias name of [User name]

Related

Defining new variable from Subquery - Only scalar expressions are allowed. (SQL Server)

I'm trying to add a column [Missed 2x] to an existing table that indicates whether the Subject has had two consecutive missed diaries. The value is "Missed 2x" if the most recent 2 diaries are missed, and NULL otherwise. I wrote a select statement (within the code below) that successfully pulls the IDs who meet this criteria. When I created a User-Defined Function to cross-check the table and assign the value, it was untenable, as the function is recalculated for every ID every time that table is queried and took forever.
I attempted to assign the value via Alter Table by seeing if the ID of the existing table is in the Subset table. I figured this should save resources since the subset table is defined once, and the Alter statement simply checks if each ID is in the subset. Then I became aware that I can't do this with Alter Table as I get the error Subqueries are not allowed in this context. Only scalar expressions are allowed.
What is the most efficient alternative?
ALTER TABLE [R3008_Subjchar] add [Missed 2x] as
case when [Subject ID] in
(
select
[Subject ID]
from(
select distinct
[Subject ID]
, [Questionnaire Name]
, [Task Date]
, [Is Completed]
, dense_rank () over (Partition by [Subject ID], [Questionnaire Name] order by [Task Date] desc) as recency
from dbo.R3008_Questionnaire
) r
where recency <=2
group by [Subject ID]
having count(case when [Is Completed] = 'no' and [Questionnaire Name] = 'FLU-PRO Questionnaire' then [Is Completed] end) = 2
)
then 'Missed 2x' end
You can't use a subquery in a computed column; it may only access the data that is on the current row, not other rows.
I don't think that storing such information is a good idea. Instead, you can create a view; lag() comes handy to implement the logic you want:
create view v_r3008_questionnaire
select q.*,
case when [Is Completed] = 'no'
and lag([Is Completed]) over(partition by [Subject ID], [Questionnaire Name] order by [Task Date]) = 'no'
then 'Missed 2x'
end as [Missed 2x]
from dbo.r3008_questionnaire
For each "failed" row, this check if the previous status for the same subject and questionnaire is failed as well, and sets the flag in that case.

Selecting additional data/values to display as column in query or in form

I have an employee index, that I need to run queries on for each employee, and display that output along with the original employee.
So say the employee index has an ID, batch, status, and multiple other columns. I have a table where I keep track of every time a column in the employee index changes. I want to display and later export dates of when certain columns use to equal other values, right along side the original employee row.
Naturally I tried creating a form to display multiple value, added a text box to hold my extra information, and changed the value of the text box for each Form_Current event in VBA.
Private Sub Form_Current()
Me.txtPhaseOne = SimpleLookup("SELECT TOP 1 ChangeDate FROM EmployeeVariables WHERE VariableName = ""Batch"" AND EmployeeID = " & Me.Recordset("ID Number") & " ORDER BY ChangeDate ASC", "ChangeDate")
End Sub
It seemed to work at first...
Until I realized the dates were set to whatever the current record date should be
[
So then I tried a join:
SELECT
EmployeeIndex.[ID Number],
EmployeeIndex.Batch,
EmployeeIndex.Status,
EmployeeIndex.[First name],
EmployeeIndex.[Last name],
EVA.ChangeDate as Phase1
FROM
EmployeeIndex
(SELECT TOP 1 ChangeDate FROM EmployeeVariables WHERE EmployeeID = EmployeeIndex.[ID Number] ORDER BY CHangeDate) EVA
Which would work, if I could some how prefetch EmployeeIndex.[ID Number]. (I didn't name these columns) Except I haven't got the slightest clue and I'm running on fumes.
SELECT
EmployeeIndex.[ID Number],
EmployeeIndex.Batch,
EmployeeIndex.Status,
EmployeeIndex.[First name],
EmployeeIndex.[Last name],
EVA.ChangeDate as Phase1
FROM
EmployeeIndex
INNER JOIN
(SELECT TOP 1 EmployeeID, ChangeDate FROM EmployeeVariables WHERE ORDER BY ChangeDate) EVA
ON EmployeeIndex.[ID Number] = EVA.EmployeeID
Try with a subquery:
SELECT
EmployeeIndex.[ID Number],
EmployeeIndex.Batch,
EmployeeIndex.Status,
EmployeeIndex.[First name],
EmployeeIndex.[Last name],
(SELECT Max(EVA.ChangeDate)
FROM EmployeeVariables AS EVA
WHERE EVA.EmployeeID = EmployeeIndex.[ID Number]) AS Phase1
FROM
EmployeeIndex
Cross Apply will be perfect for your TOP 1 case, as it will run for each employeeID rather than just joining on 1

MS Access SQL Sum without GROUP BY

I am trying to add a Sum 'field' to a SELECT query, where it sums up the data on that row, and returns it as a field. The problem seems to lie with the GROUP BY statement, that I seemingly have to use. When using this, it groups the 'sums' together, rather than provides a total for each row of data.
SELECT PS_DB.TeamName AS [Team Name], TM_adjData.SM_adjName AS Adjudicator, PS_DB.WeekEnding AS [Week Ending], PS_DB.Pts AS [BAU Points], PS_DB.Adhc, Sum(PS_DB.Pts + PS_DB.Adhc) as [Total], PS_DB.Approved AS Approved
FROM PS_DB
LEFT JOIN TM_adjData on PS_DB.Adjudicator = TM_adjData.SM_empNum
GROUP BY TeamName, SM_adjName, WeekEnding, Pts, Adhc, Approved
This returns 518 rows, where as if I remove the GROUP BY section and the 'sum' field, it returns 1,608 rows (which is correct).
How can I get the 1,608 rows with the sum next to it?
I think you can do what you want with a correlated subquery:
SELECT p.TeamName AS [Team Name], a.SM_adjName AS Adjudicator,
p.WeekEnding AS [Week Ending], p.Pts AS [BAU Points], p.Adhc,
(SELECT SUM(p2.Pts + p2.Adhc)
FROM PS_DB as p2
WHERE p2.TeamName = p.TeamName -- perhaps more conditions are needed
) as [Total],
p.Approved AS Approved
FROM PS_DB as p LEFT JOIN
TM_adjData as a
ON p.Adjudicator = a.SM_empNum;
If you want to perform a sum for each row, you don't group rows, instead you simply perform addition. I've added Nz() function to properly address issue where any of the value being added is null and treat it as 0:
SELECT
PS_DB.TeamName AS [Team Name],
TM_adjData.SM_adjName AS Adjudicator,
PS_DB.WeekEnding AS [Week Ending],
PS_DB.Pts AS [BAU Points],
PS_DB.Adhc,
Nz(PS_DB.Pts,0) + Nz(PS_DB.Adhc,0) as [Total], -- this is your row sum
PS_DB.Approved AS Approved
FROM PS_DB
LEFT JOIN TM_adjData on PS_DB.Adjudicator = TM_adjData.SM_empNum
SUM is an aggregate function and it works on entire table or groups of data (with GROUP BY).

MS Access: First () function in query not retrieving correct values

I have 4-6 tables, but the main ones are LRTTable and TempTable. They are joined using INNER JOIN and result set is LEFT JOIN with other 6 tables.
LRTable and Temp Table contains Store ID ,PCC Id ,COF NO, Line of Business Name, Status, Address and few other columns. LRTable can have duplicate records for same Store ID with different COF No(0,7987987) and Line of Business Name(Food and Retail).
Temp Table has Store ID, PCC. Combination of these two are primary key in Access.
Query is correctly able to pull all the data from LRTable based on TempTable. For example, query pulls all the records (including duplicates)from LRtable when there is match in Temp Table and are sorted based on status code and line of business. By doing so I am ensuring the active and food records are available on the top.
What I need to achieve is, instead of having multiple records for each store in the query result set, I want only top record from set of records of each store from the result set. I am able to this with the first function, however its picking values fron LRTable rather than sub query result
Is there a way I can just pick from sub query ?
LRTable:
Store ID PCC ID Status Line of Business Name COF No
---------- -------- ---------- -----------------------
1 123 Active Food 999
1 123 InActive Food 89899
1 123 Active Retail 0
2 222 Active Retail 0
2 222 InActive food 76767
TempTable
Store ID : 1 and 2
PCC : 123 and 222
Result:
- 1,0
- 2,0
Expected output:
- 1,999
- 2,76767
Below is the query with first
SELECT StoreID,FIRST (COF No) from
(
select * from
(
SELECT distinct TempTable.[Avendra ID],
TempTable.[Marsha Code],
LRTable.[Concept Name],
LRTable.[Outlet (Store) Name],
LRTable.[Outlet Street Address Text ID],
LRTable.[Outlet City ID],
LRTable.[Outlet State ID],
LRTable.[Outlet Zip Code ID],
LRTable.[Status Code ID],
LRTable.[Outlet SAP ID ID],
LRTable.[Outlet PCC ID ID],
LRTable.[COF Number ID],
CLPSource.[Brand Mandated],
FROM (
(
(
(
(
(
LRTable INNER JOIN TempTable
ON LRTable.[Outlet PCC ID ID] = TempTable.[PCC ID]
AND LRTable.[Store Number ID] = TempTable.[Store Number ID]
) LEFT JOIN CLPSource
ON TempTable.[Avendra ID] = CLPSource.[Avendra Customer ID]
) LEFT JOIN OasisReportSource
ON TempTable.[Marsha Code] = OasisReportSource.[Marsha])
ORDER BY LRTable.[Status Code ID],
LRTable.[Line of Business Name]))
Group by LRTable.[StoreID]
Using the data, actual output and desired output you provide in your LRTemp table and Temp Table panels, I reckon can be done using the FIRST aggregate function i.e.
SELECT StoreID, FIRST(PCC ID), FIRST(Status), FIRST(Line of Business Name), FIRST (COF No)
FROM (joined LRTable and TempTable)
GROUP BY StoreId
You should then get the data of first-occurring record for each StoreId.

Entire Union query returns no results if part of it returns no results

I've created the folllowing Union query, which works fine most of the time:
SELECT [%$###_Alias].[Contact ID], [%$###_Alias].[Mailing Name]
FROM (SELECT [Referrals - Contacts Within Organisations].[Contact ID], [Referrals - Contacts Within Organisations].[Mailing Name], [Referrals - Contacts Within Organisations].[Surname], [Referrals - Contacts Within Organisations].[First name]
FROM [Referrals - Contacts Within Organisations]
UNION SELECT "0" as [Contact ID], "View all contacts" as [Mailing Name], "0" as [Surname], "0" as [First name]
FROM [Referrals - Contacts Within Organisations]) AS [%$###_Alias]
ORDER BY [%$###_Alias].Surname, [%$###_Alias].[First name];
This adds an initial row of "View all contacts" at the top of whatever the query returns.
However, if the "actual" query part of it returns no results, the entire query returns no results, whereas I'd always want the initial row to appear regardless.
Is this possible, and if so, what am I doing wrong? Thanks.
EDIT: Thanks all for your help. The final working query is below for reference of anyone else who needs this sort of thing:
SELECT A.[Contact ID], A.[Mailing Name]
FROM (SELECT "0" as [Contact ID], "View all contacts" as [Mailing Name], "0" as [Surname], "0" as [First name]
FROM [Dummy]
UNION
SELECT [Referrals - Contacts Within Organisations].[Contact ID], [Referrals - Contacts Within Organisations].[Mailing Name], [Referrals - Contacts Within Organisations].[Surname], [Referrals - Contacts Within Organisations].[First name]
FROM [Referrals - Contacts Within Organisations]) AS A
ORDER BY A.Surname, A.[First name];
To return always one row with data in SQL, even when you are manually providing the values, you need to be selecting from a table that has at least one row, I suggest using a tblDummy with one field of random information.
e.g:
SELECT "Hello", "Goodbye"
FROM tblDummy
As a side note I would also try and get rid of:
[%$###_Alias]
This can be accomplished by aliasing your Derived tables by adding:
(SELECT * FROM Blah) AS A
for example to name the derived table as A