I am new and learning SQL on Microsoft SQL Server 2008. I am trying to do the following for a table of order line items each record is one order item.
any transaction numbers with only one record and a code of #####.
I have tried many ways but haven't been able to figure it out
Select * from Table
where count(transactionnumber)<2 and Code='9987'
I think I got it. Had to use a different code because QA didn't have an distinct rows with that code. Let me know if you see anything that might cause and issue.
Select * from (select Orders.TransactionNumber from Orders
group by Orders.TransactionNumber
having COUNT (Orders.TransactionNumber)=1) as transa
Inner join Orders on transa.TransactionNumber=Orders.TransactionNumber
where ItemCode=9803
Ended up with this code
use XXX
Select Orders.TransactionNumber,Orders.RepNumber, Orders.CustomerID,Orders.ShipToId,orders.ItemCode,Orders.Quantity,Orders.ReceivedDate,Orders.TransmitStatus from (select TransactionNumber from Orders
group by TransactionNumber
having COUNT (TransactionNumber)=1) as transa
Inner join Orders on Orders.TransactionNumber=transa.TransactionNumber
where ItemCode=9987 and ReceivedDate > DateADD (day, -1, GetDate() )
IF ##ROWCOUNT > 0
BEGIN
EXEC msdb.dbo.sp_send_dbmail
recipients=N'XXXX',
#body='Merchandisers orders with only Item Code 9803',
#subject ='only Item Code 9803',
#profile_name ='',
#query = 'Select Orders.TransactionNumber,Orders.RepNumber,Orders.CustomerID,Orders.ShipToId,orders.ItemCode,Orders.Quantity,Orders.ReceivedDate,Orders.TransmitStatus from(select TransactionNumber from Orders
group by TransactionNumber
having COUNT (TransactionNumber)<2) as transa
Inner join Orders on Orders.TransactionNumber=transa.TransactionNumber
where ItemCode=9803 and ReceivedDate > DateADD (day, -1, GetDate() )'
END'
Your query should include something to group by (Name, TransactionNumber, etc.) do a count and constrain on that.
SELECT TransactionCode, COUNT(*)
FROM TABLE
GROUP BY TransactionCode
HAVING COUNT(*) < 2
AND ItemCode = 9987
You may consider using the OVER keyword, so that you don't need to muck around the count(*) and group by so much. That way you can see counts without grouping.
Select *, count(*) over (partition by transactioncode) as "TheCounts"
from table
--where itemcode = 9903
or
Select *, count(*) over (partition by transactioncode, itemcode) as "TheCounts"
from table
--where itemcode = 9903
Here is what I ended up with on this that seems to be working correctly for about a week now it executes a sp_send_dbmail only if there are records.
Select Orders.TransactionNumber, Orders.RepNumber, Orders.CustomerID,Orders.ShipToId,orders.ItemCode,Orders.Quantity,Orders.ReceivedDate,Orders.TransmitStatus from (select TransactionNumber from Orders
group by TransactionNumber
having COUNT (TransactionNumber)=1) as transa
Inner join Device_Orders on Orders.TransactionNumber=transa.TransactionNumber
where ItemCode=9987 and ReceivedDate > DateADD (day, -1, GetDate() )
IF ##ROWCOUNT > 0
Related
I have the following query where I input a date and it give me the result. However, I need to run this for 60 different dates. Instead of running this 1 by 1, is there anyway to automate this so it runs each time on a different date?
IF OBJECT_ID('tempdb..#1') IS NOT NULL DROP TABLE #1
declare #d1 datetime = '2020-02-06'
select distinct [User] into #1
from [X].[dbo].[Table1]
where [status] = 'Success'
and [Date] = #d1;
select count(distinct [User])
from #1
inner join [Y].[dbo].[Table2]
on #1.[User] = [Y].[dbo].[Table2].User
where [Date2] between #d1 and #d1+1
and [Checkname] in ('Check1','Check2')
Loops are slow and generally a bad practice in the context of T-SQL. You can use something like this to get the count of users for a batch of dates:
DROP TABLE IF EXISTS #DataSource;
CREATE TABLE #DataSource
(
[Date] DATETIME
,[UsersCount] INT
);
INSERT INTO #DataSource ([Date])
VALUES ('2020-02-06')
,('2020-02-07')
,('2020-02-08');
IF OBJECT_ID('tempdb..#1') IS NOT NULL DROP TABLE #1
select distinct DS1.[Date]
,DS1.[User]
into #1
from [X].[dbo].[Table1] DS1
INNER JOIN #DataSource DS2
ON DS1.[Date] = DS2.[Date]
where DS1.[status] = 'Success';
select #1.[date]
,count(distinct [User])
from #1
inner join [Y].[dbo].[Table2]
on #1.[User] = [Y].[dbo].[Table2].User
where [Date2] between #1.[date] and #1.[date] + 1
and [Checkname] in ('Check1','Check2')
GROUP BY #1.[date]
First, I want to say that gotqn's answer is a good answer - however, I think there are a few more things in the original code that can be improved - so here is how I would probably do it:
Assuming the dates are consecutive, use a common table expression to calculate the dates using dateadd and row_number.
Then, use another common table expression to get the list of dates and users from table1,
and then select the date and count of distinct users for each date from that common table expression joined to table2:
DECLARE #StartDate Date = '2020-02-06';
WITH Dates AS
(
SELECT TOP (60) DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY ##SPID) -1, #StartDate) As Date
FROM sys.objects
), CTE AS
(
SELECT t1.[User], t1.[Date]
FROM [X].[dbo].[Table1] AS t1
JOIN Dates
ON t1.[Date] = Dates.[Date]
WHERE [status] = 'Success'
)
SELECT cte.[Date], COUNT(DISTINCT [User])
FROM CTE
JOIN [Y].[dbo].[Table2] As t1
ON CTE.[User] = t1.[User]
AND t1.[Date2] >= CTE.[Date]
AND t1.[Date2] < DATEADD(Day, 1, CTE.[Date])
AND [Checkname] IN ('Check1','Check2')
GROUP BY cte.[Date]
If the dates are not consecutive, you can use a table variable to hold the dates instead of calculating them using a common table expression.
In my SQL Server Query, I am trying to count the number of employees per site. This works, but when I try to add in a percentage of total, it still groups by Site so it is inaccurate.
Is there an easier way to achieve this?
I am using this Query to create a view.
select Site.SiteName,
sum(case when Employee.ActiveStatus = 'Yes' then 1 else 0 end) as
"NumberOfEmployees",
CONVERT(decimal(6,2),(sum(case when Employee.ActiveStatus = 'Yes' then 1
else 0 end))/(convert(decimal(6,2),COUNT(EmployeeID)))) as PercentageOfEmps
from Employee
left join Site
on(Employee.SiteID=Site.SiteID)
GROUP BY Site.SiteName;
GO
You could use subquery:
select
Site.SiteName,
NumberOfEmployees = sum(case when Employee.ActiveStatus = 'Yes' then 1 else 0 end),
PercentageOfEmps = CONVERT(decimal(6,2),(sum(case when Employee.ActiveStatus = 'Yes' then 1
else 0 end))/(SELECT COUNT(EmployeeID) FROM Employee)
from Employee
left join Site
on Employee.SiteID=Site.SiteID
GROUP BY Site.SiteName;
I can't provide an answer for your scenario, as I don't have any sample data to use, therefore I've provided a small dataset.
One method is to use a CTE/Subquery to get a total number and then include the total in the GROUP BY. This method avoids 2 scans of the table:
WITH VTE AS(
SELECT *
FROM (VALUES(1,'Steve',1),
(2,'Jayne',1),
(3,'Greg',2),
(4,'Sarah',3)) V(EmpID, EmpName, SiteID)),
CTE AS(
SELECT V.EmpID,
V.EmpName,
V.SiteID,
COUNT(V.EmpID) OVER () AS TotalCount
FROM VTE V)
SELECT C.SiteID,
COUNT(C.EmpID) AS Employees,
COUNT(C.EmpID) / (C.TotalCount *1.0) AS Perc
FROM CTE C
GROUP BY C.SiteID,
C.TotalCount;
This script should help-
SELECT
Site.SiteName,
COUNT(EmployeeID) AS [NumberOfEmployees],
((COUNT(EmployeeID)*1.0)/(SELECT COUNT(*) FROM Employee WHERE ActiveStatus = 'Yes'))*100.00 as PercentageOfEmps
FROM Employee
INNER JOIN Site
ON Employee.SiteID = Site.SiteID
WHERE Employee.ActiveStatus = 'Yes'
GROUP BY Site.SiteName;
Data creation script
declare #Employee Table(EmployeeID int ,ActiveStatus nvarchar(20) ,SiteID int)
declare #Site Table(SiteName nvarchar(20) ,SiteID int)
insert into #Employee values(1,'Yes',101),(2,'Yes',101),(3,'Yes',102),(4,'Yes',102),
(5,'Yes',101)
insert into #Site values('Site1',101)
insert into #Site values('Site2',102)
//real script to get the %percentage
;with cte as
(
select s.SiteName,sum(case when e.ActiveStatus = 'Yes' then 1 else 0 end) as "NumberOfEmployees"
from #Employee e
left join #Site s
on(e.SiteID=s.SiteID)
GROUP BY s.SiteName
),
cte_sum as
(select sum(NumberOfEmployees) as total from cte )
select c.*, convert (decimal(6,2),c.NumberOfEmployees)/convert (decimal(6,2),cs.total)*100 from cte_sum cs, cte c;
In the image above you'll note the first 6 rows that shows 2 records for each StudentID. I am needing to UPDATE, for each group of StudentID's, the EndDate column value of the 2nd record with a value of ONE DAY LESS THAN the StatusEffectiveDate of the first record for the same StudentID. I am working with SQL Server 2014.
So the first record should look like:
Here is my SQL code:
SELECT *
FROM #TEMP_99RecordsNeedingEndDateUpdated
ORDER BY StudentID asc, StatusEffectiveDate desc
Any help/direction would be appreciated.
Using what ZLK stated with a CTE to update the rows.
;WITH cte AS
(SELECT
*
, DATEADD(DAY, -1, LAG(statuseffectivedate) OVER (PARTITION BY StudentID ORDER BY statuseffectivedate DESC)) AS NewDate
FROM #TEMP_99RecordsNeedingEndDateUpdated)
UPDATE cte
SET cte.enddate = cte.NewDate
WHERE cte.NewDate IS NOT NULL
Late to the party, but here's one to try as well:
BEGIN TRANSACTION
UPDATE [YourTable] SET EndDate = DATEADD(DAY, -1, t.MaxDate)
FROM
(
SELECT StudentID, MAX(StatusEffectiveDate) as MaxDate
FROM [YourTable]
GROUP BY StudentID
) t
WHERE [YourTable].StudentID = t.StudentID AND [YourTable].StatusEffectiveDate <> t.MaxDate
-- COMMIT TRANSACTION
-- ROLLBACK TRANSACTION
As others have stated, this has very limited constraints on your data.
Essentially, I'm updating the EndDate to a new value, based on a sub query for the MAX() date from the same student ID. Finally, to avoid updating all student records, I ensure that the StatusEffectiveDate is not the later date.
This has very limited constraints, notably always needing two rows - one 'Free' and one 'Other' - but if that is the case, you should be able to do a UNION query.
SELECT StudentID, Location, Status, EconDisCode, StatusEffDate, EndDate
FROM #TEMP_99RecordsNeedingEndDateUpdated
WHERE Status = 'Free'
UNION
SELECT StudentID, Location, Status, EconDisCode, StatusEffDate, (SELECT StatusEffDate-1 FROM #TEMP_99RecordsNeedingEndDateUpdated t2 WHERE Status = 'Free' AND t1.StudentID = t2.StudentID) AS EndDate
FROM #TEMP_99RecordsNeedingEndDateUpdated t1
WHERE Status = 'Other'
I think a fiddle example with the data would be helpful.
with cte as
( select *
, row_number over (partition by studentID order by StatusEffectiveDate desc ) as rnD
, row_number over (partition by studentID order by StatusEffectiveDate asc ) as rnA
from table t
)
select cte.StudentID, cte.Location, cte.Status, cte.EconDisCode, cte.StatusEffectiveDate
, cte.EndDate
from cte
where rnD == 1
union
select cteA.StudentID, cteA.Location, cteA.Status, cteA.EconDisCode, cteA.StatusEffectiveDate
, dateadd(d, -1, cteD.StatusEffectiveDate)
from cte cteA
join cte cteD
on cteA.StudentID = cteD.StudentID
and cteA.rnD = 1
and cteD.rnA = 1
order by studentID, StatusEffectiveDate desc
update cteA
set cteA.EndDate = dateadd(d, -1, cteD.StatusEffectiveDate)
from cte cteA
join cte cteD
on cteA.StudentID = cteD.StudentID
and cteA.rnD = 1
and cteD.rnA = 1
order by studentID, StatusEffectiveDate desc
Include actual text (not image) so can cut and paste
I want to select all records for customers whose first order is from 2015. I want any orders they placed after 2015 too, but I DON'T want the records for customers whose first order was in 2016. I am ultimately trying to find the percentage of people who order more than twice, but I want to exclude the customers who were new in 2016.
This doesn't work because 'mindate' is an invalid column name but I'm not sure why or how else to try it.
Select
od.CustomerID, OrderID, OrderDSC, OrderDTS
From
OrderDetail OD
Join
(Select
OrderID, Min(orderdts) as mindate
From
OrderDetail
Where
mindate Between '2015-1-1' and '2015-12-31'
Group By Orderid) b on od.OrderID = b.OrderID
Because execution phases - it's seqency how is qry evaluated and by engine. In where clause your mindate not yet exists.
You can change mindate by orderdts:
select OrderID, min(orderdts) as mindate
from OrderDetail
where orderdts between '2015-1-1' and '2015-12-31'
group by Orderid
Second option is to use having statement - it's evaluated after group by.
What I di was select the distinct CustomerIDs that fall in between your daterange and did a left join with the table so it filters out anyone that doesn't fall in between your daterange.
SELECT * FROM
(Select DISTINCT(CustomerID) as CustomerID
FROM OrderDetail WHERE OrderDTS between '2015-1-1' AND '2015-12-31') oIDs
LEFT JOIN
OrderDetail OD
ON oIDs.CustomerID = OD.CustomerID
Try using the EXISTS clause. It is basically a sub-query. Below is an example you should be able to adapt.
create table Test (Id int, aDate datetime)
insert Test values (1,'04/04/2014')
insert Test values (1,'05/05/2015')
insert Test values (1,'06/06/2016')
insert Test values (2,'04/30/2016')
insert Test values (3,'02/27/2014')
select t.* from Test t
where
aDate>='01/01/2015'
and exists(select * from Test x where x.Id=t.Id and x.aDate >='01/01/2015' and x.aDate<'01/01/2016')
I don't know the orderdts data type but if it is datetime orders on 2015-12-31 will not be included (unless the order date is 2015-12-31 00:00:00.000. Note how this will skip the first record:
DECLARE #orders TABLE (CustomerID INT, orderDate DATETIME);
INSERT #orders VALUES (1, '2015-12-31 00:00:01.000'), (1, '2015-12-30'), (2, '2015-01-04');
SELECT * FROM #orders WHERE orderDate BETWEEN '2015-01-01' AND '2015-12-31';
In this case you would want the WHERE clause filter to look like:
WHERE orderDate BETWEEN '2015-01-01 00:00:00.000' AND '2015-12-31 23:59:59.999';
Or
WHERE CAST(orderDate AS date) BETWEEN '2015-01-01' AND '2015-12-31';
(the first example will almost certainly perform better).
Now, using this sample data:
-- Sample data
CREATE TABLE #LIST (LISTName varchar(10) NOT NULL);
INSERT #LIST
SELECT TOP (100) LEFT(newid(), 8)
FROM sys.all_columns a, sys.all_columns b;
-- You will probably want LISTName to be indexed
CREATE NONCLUSTERED INDEX nc_LISTName ON #LIST(LISTName);
You can implement Paul's solution like this:
DECLARE #LIST_Param varchar(8) = 'No List';
SELECT LISTName
FROM
(
SELECT distinct LISTName
FROM #LIST
UNION ALL
SELECT 'No List'
WHERE (SELECT COUNT(DISTINCT LISTName) FROM #LIST) < 1000000
) Distinct_LISTName
WHERE (#LIST_Param = 'No List' or #LIST_Param = LISTName);
Alternatively you can do this:
DECLARE #LIST_Param varchar(8) = 'No List';
WITH x AS
(
SELECT LISTName, c = COUNT(*)
FROM #LIST
WHERE (#LIST_Param = 'No List' or #LIST_Param = LISTName)
GROUP BY LISTName
),
c AS (SELECT s = SUM(c) FROM x)
SELECT LISTName
FROM x CROSS JOIN c
WHERE s < 1000000;
I am putting together a query in SQL Server but having issues with the sub-query
I wish to use the max(loadid) and count the number of records the query returns.
So for example my last loadid is 400 and the amount of records with 400 is 2300, so I would my recor_count column should display 2300. I have tried various ways below but am getting errors.
select count (loadid)
from t1
where loadid = (select max(loadid) from t1) record_count;
(select top 1 LOADID, count(*)
from t1
group by loadid
order by count(*) desc) as Record_Count
Showing loadid and number of matching rows with the use of grouping, ordering by count and limiting the output to 1 row with top.
select top 1 loadid, count(*) as cnt
from t1
group by loadid
order by cnt desc
This may be easier to achieve with a window function in the inner query:
SELECT COUNT(*)
FROM (SELECT RANK() OVER (ORDER BY loadid DESC) AS rk
FROM t1) t
WHERE rk = 1
Another simplest way to achieve the result :
Set Nocount On;
Declare #Test Table
(
Id Int
)
Insert Into #Test(Id) Values
(397),(398),(399),(400)
Declare #Abc Table
(
Id Int
,Value Varchar(100)
)
INsert Into #Abc(Id,Value) Values
(398,'')
,(400,'')
,(397,'')
,(400,'')
,(400,'')
Select a.Id
,Count(a.Value) As RecordCount
From #Abc As a
Join
(
Select Max(t.Id) As Id
From #Test As t
) As v On a.Id = v.Id
Group By a.Id