I have two tables in SQL Server database - users and vacation - sql-server

I want to select all users from the users' table and their data from the vacation table as start vacation date and end date to get their vacation balance and if the user has no vacation taken return 0
Here is my query:
SELECT
dbo.USERINFO.USERID,
dbo.USERINFO.NAME AS إسم_الموظف,
SUM(DATEDIFF(d, dbo.vacation.STARTSDAY, dbo.vacation.ENDSDAY) + 1) AS عددالايام,
dbo.userinfo.balance - (SUM(DATEDIFF(d, dbo.vacation.STARTSDAY, dbo.vacation.ENDSDAY) + 1)) AS الرصيد
FROM
dbo.USERINFO
LEFT JOIN
dbo.Vacation ON dbo.Vacation.UserID = dbo.USERINFO.USERID
WHERE
dbo.USERINFO.DEFAULTDEPTID <> 7
AND dbo.vacation.DATEID = 2
AND year(dbo.vacation.STARTSDAY) = 2020
AND month(dbo.vacation.STARTSDAY) BETWEEN 1
AND 12
GROUP BY
dbo.USERINFO.userid,
dbo.USERINFO.NAME,
dbo.userinfo.balance

You need to move the condition inside the case when statement while calculating the sum
SELECT U.USERID,
U.NAME AS إسم_الموظف,
ISNULL(SUM(CASE WHEN V.DATEID = 2
AND year(V.STARTSDAY) = 2020
THEN DATEDIFF(d, V.STARTSDAY, V.ENDSDAY)
ELSE 0
END), 0) AS عددالايام,
ISNULL(U.balance, 0) - ISNULL(SUM(CASE WHEN V.DATEID = 2
AND year(V.STARTSDAY) = 2020
THEN DATEDIFF(d, V.STARTSDAY, V.ENDSDAY)
ELSE 0
END), 0) AS الرصيد
FROM dbo.USERINFO AS U
LEFT JOIN dbo.Vacation AS V ON V.UserID = U.USERID
WHERE U.DEFAULTDEPTID <> 7
GROUP BY U.userid,
U.NAME,
U.balance
Results:
+--------+------------+-----------+--------+
| USERID | إسم_الموظف | عددالايام | الرصيد |
+--------+------------+-----------+--------+
| 1 | NAME 1 | 8 | 2 |
+--------+------------+-----------+--------+
| 2 | NAME 2 | 6 | 4 |
+--------+------------+-----------+--------+
| 4 | NAME 4 | 0 | 10 |
+--------+------------+-----------+--------+
The script used to generate the dummy data is:
CREATE TABLE USERINFO (USERID int, NAME VARCHAR(20), BALANCE INT, DEFAULTDEPTID INT)
INSERT INTO USERINFO VALUES
(1, 'NAME 1', 10, 5),
(2, 'NAME 2', 10, 4),
(3, 'NAME 3', 10, 7),
(4, 'NAME 4', 10, 5)
CREATE TABLE Vacation (UserID INT, STARTSDAY DATETIME, ENDSDAY DATETIME, DATEID INT)
INSERT INTO Vacation VALUES
(1, '2020-01-12', '2020-01-20', 2),
(1, '2020-01-22', '2020-01-24', 3),
(2, '2020-01-27', '2020-01-31', 2),
(2, '2020-03-27', '2020-03-29', 2),
(7, '2020-03-27', '2020-03-29', 2)
Note that the UserId 3 is retired so it is not included in the result.
Note that vacation with DateId 3 not included when calculating the sum

Related

Query items that don't have a related record in link table but return results with Id from link table

I have an Item table:
Id | Title | Active
====================
1 | Item 1 | 1
2 | Item 2 | 1
A Location table:
Id | Name
=========
1 | A1
2 | B1
and a link table, where EventId specifies a cycle count event:
Id | EventId | ItemId | LocationId
=============|====================
1 | 1 | 1 | 2
2 | 1 | 2 | 1
3 | 2 | 1 | 1
4 | 2 | 2 | 2
5 | 3 | 1 | 1
I need to determine what items haven't been cycle-counted for a specified EventId (which in this example would be ItemId 2 for EventId 3). We're using a code generation tool that only supports tables and views with a simple filter, so I can't use a sproc or table-valued function. Ideally we'd like to be to do this:
SELECT [EventId], [ItemId] FROM [SomeView] WHERE [EventId] = 3
and get a result like
EventId | ItemId
================
3 | 2
I've tried to wrap my head around this -- unsuccessfully -- because I know it's difficult to query a negative. Is this even possible?
Is something like the following what you're after?
select l.eventId, x.Id ItemId
from Link l
cross apply (
select *
from Items i
where i.Id != l.ItemId
)x
where l.EventId = 3;
--data to work with
DECLARE #items TABLE (ID int, Title nvarchar(100), Active int)
INSERT INTO #items VALUES (1, 'Item 1', 1)
INSERT INTO #items VALUES (2, 'Item 2', 1)
DECLARE #location TABLE (ID int, Name nvarchar(100))
INSERT INTO #location VALUES (1, 'A1')
INSERT INTO #location VALUES (2, 'B1')
DECLARE #linkTable TABLE (ID int, EventId int, ItemId int, LocationId int)
INSERT INTO #linkTable VALUES (1, 1, 1, 2)
INSERT INTO #linkTable VALUES (2, 1, 2, 1)
INSERT INTO #linkTable VALUES (3, 2, 1, 1)
INSERT INTO #linkTable VALUES (4, 2, 2, 2)
INSERT INTO #linkTable VALUES (5, 3, 1, 1)
INSERT INTO #linkTable VALUES (6, 4, 2, 1)
--query you want
SELECT 3 as EventID, ID as ItemID
FROM #items i
WHERE ID not in (SELECT ItemId
FROM #linkTable
WHERE EventId = 3)
Get all the ItemIDs from the LinkTable and then get all the items from the Items table that dont have the sync event. You can replace the 3 in WHERE and SELECT clauses with whatever event you are looking for. And if you want all such pairs of event + item then this should do it:
SELECT subData.EventId, subData.ItemID
FROM (SELECT i.ID as ItemID, cj.EventId
FROM #items i CROSS JOIN (SELECT DISTINCT EventId
FROM #linkTable) cj) subData
left join #linkTable lt ON lt.EventId = subData.EventId and lt.ItemId = subData.ItemID
WHERE lt.ID is null
This could be heavy on performance because CROSS JOIN and DISTINCT and subjoins but it gets the job done. At 1st you create a data of all possible items and events pairs, then left join linked table to it and if the linked table's ID is null that means that there is no event + item pair which means that the item is not synced for that event.

SELECT only row where all other activity are completed

I have a table with column called , ID,Activity,Activity_type,Activity Status.
Here activity status 11 = Completed, 12 = Incomplete.
Each ID is associated with activity type call1,call2,email1,email2 and text1. I only want to query all the ID with activity Text1 with "Activity Status"=12 when all other "Activity Status" related to that ID = 11.
In the example I would like to get output of Text1 with ID 10 as ID 11 still has email 2 with activity status 12. Any help on this is much appreciated.
Using GROUP BY and HAVING should be a working solution, but your question probably has two possible answers:
case, when "Activity Status" = 12 and all other (all the rest) "Activity Status" = 11
case, when "Activity Status" = 12 and all other (all possible four) "Activity Status" = 11
Input:
CREATE TABLE #Data
(
ID int,
Activity varchar(10),
Activity_type int,
Activity_status int
)
INSERT INTO #Data
(ID, Activity, Activity_type, Activity_status)
VALUES
(10, 'call1', 10893, 11),
(10, 'call2', 10894, 11),
(10, 'email1', 10895, 11),
(10, 'email2', 10896, 11),
(10, 'text1', 10897, 12),
(11, 'call1', 10893, 11),
(11, 'call2', 10894, 11),
(11, 'email1', 10895, 11),
(11, 'email2', 10896, 12),
(11, 'text1', 10897, 12)
T-SQL (for all the rest):
SELECT ID
FROM #Data
GROUP BY ID
HAVING
SUM(CASE WHEN Activity = 'text1' AND Activity_status = 12 THEN 1 END) = 1 AND
SUM(1) - SUM(CASE WHEN Activity <> 'text1' AND Activity_status = 11 THEN 1 END) = 1
T-SQL (for all possible four):
SELECT ID
FROM #Data
GROUP BY ID
HAVING
SUM(CASE WHEN Activity = 'text1' AND Activity_status = 12 THEN 1 END) = 1 AND
SUM(CASE WHEN Activity IN ('call1', 'call2', 'email1', 'email2') AND Activity_status = 11 THEN 1 END) = 4
Output:
---
ID
---
10
You can use group by and having:
select id
from t
group by id
having sum(case when activity = 'Text1' and activity_status = 12 then 1 else 0 end) > 0 and
sum(case when activity <> 'Text1' and activity_status <> 11 then 1 else 0 end) = 0;

SQL Pivot Using three tables

I am running into some trouble trying to pivot some data out of SQL.
I have three tables that will comprise the data.
Table 1: (Clause)
-Clause
-ClauseName
Table 2: (Process)
-Id
-ProcessName
Table 3: (RELProcessClauses)
-ProcessId
-Clause
-WeightedValue
Ultimately, I am looking to have a matrix of data that is Clause, ClauseName down the left, ProcessName across the top and the Weighted value to correspond between Process and Clause.
Not sure if this will make much sense.
Join the three tables and use PIVOT on it. You can run the following query:
SELECT * FROM (
SELECT
c.Clause,
c.ClauseName,
p.ProcessName,
pc.WeightedValue
from RELProcessClauses pc
JOIN Clause c on pc.clause = c.clause
JOIN Process p on pc.ProcessId = p.id
) x
PIVOT (
SUM(WeightedValue)
FOR ProcessName IN ([ProcessName1], [ProcessName2], [ProcessName3])
) as pvt
Output table:
+--------+-------------+--------------+--------------+--------------+
| Clause | ClauseName | ProcessName1 | ProcessName2 | ProcessName3 |
+--------+-------------+--------------+--------------+--------------+
| 1 | ClauseName1 | 10 | 15 | 30 |
| 2 | ClauseName2 | 15 | 20 | 30 |
| 3 | ClauseName3 | 20 | 20 | 30 |
+--------+-------------+--------------+--------------+--------------+
The query/output works on the demo tables created using the query below:
CREATE TABLE Clause (
Clause int,
ClauseName varchar(255)
);
CREATE TABLE Process (
Id int,
ProcessName varchar(255)
);
CREATE TABLE RELProcessClauses (
ProcessId int,
Clause int,
WeightedValue int
);
INSERT INTO Clause VALUES
(1, 'ClauseName1'),
(2, 'ClauseName2'),
(3, 'ClauseName3');
INSERT INTO Process VALUES
(1, 'ProcessName1'),
(2, 'ProcessName2'),
(3, 'ProcessName3');
INSERT INTO RELProcessClauses VALUES
(1, 1, 10),
(1, 2, 15),
(1, 3, 20),
(2, 1, 15),
(2, 2, 20),
(2, 3, 20),
(3, 1, 30),
(3, 2, 30),
(3, 3, 30);

how to use result of function as column name in where clause sql

Table structure like this
select * from TimeTable;
userid | 1am|2am|3am| 4am| 5am
1002 | 1 |1 |1 | 1 | 1
1003 | 1 |1 |1 | 1 | 1
1004 | 1 |1 |1 | 1 | 1
1005 | 1 |1 |1 | 1 | 1
I want select users that have column value 1 of specific timecolumn
I have used following query but it is throwing error
com.microsoft.sqlserver.jdbc.SQLServerException: Conversion failed
when converting the varchar value '[ 3PM]' to data type int.
select * from UserDetail u,TimeTable t
where u.userid=t.userid
and CONCAT(SUBSTRING(CONVERT(VARCHAR, getdate(), 100), 13, 2) ,'',RIGHT(CONVERT(VARCHAR, GETDATE(), 100),2)) = 1
Like this
when I use hardcoded column name then it works fine
I want to select a column name dynamically.
select * from UserDetail u,TimeTable t
where u.userid = t.userid
and [3AM] = 1
Option 1
Use a long list of short-circuited WHERE clauses like below which seems fine given the table design and quite easy to understand
DECLARE #currentHour int = DATEPART(HOUR, getdate())
SELECT * FROM TimeTable
WHERE
(#currentHour = 1 AND [1am] = 1) OR
(#currentHour = 2 AND [2am] = 1) OR
(#currentHour = 3 AND [3am] = 1) OR
(#currentHour = 4 AND [4am] = 1) OR
(#currentHour = 5 AND [5am] = 1) -- Etc
Option 2
Use UNPIVOT or VALUES to rotate the hour based columns into rows. As part of the rotate you can translate the column into a number indicating the hour. This you can compare with the current time’s hour component.
Option 3
Use dynamic sql which might or might not be ok for your environment or usage.
Below is the UNPIVOT approach (Option 2) which is a bit more complex to understand.
create table Test (id int, [1am] int, [2am] int, [3am] int, [4am] int)
insert Test values
(1, 1, 2, 3, 4)
, (2, 11, 12, 13, 14)
, (3, 1, 21, 23, 24)
, (4, 31, 32, 33, 34)
declare #time datetime = '2018-01-01 01:10:00' -- change this to getdate()
;WITH MatchingIds (id) AS
(
SELECT id
FROM
(SELECT
id,
[1am] AS [1], [2am] AS [2], [3am] AS [3], [4am] AS [4] --etc
FROM Test) AS Source
UNPIVOT
(val FOR hour IN
([1], [2], [3], [4]) --etc
) AS asRows
WHERE
hour = CONVERT(VARCHAR, DATEPART(HOUR, #time))
AND val = 1
)
SELECT * FROM MatchingIds
-- MatchingIds now contains the rows that match your criteris
-- This can be joined with other tables to generate your full result
Intermediate output from MatchingIds for above example with time param set to around 1am
| id |
|----|
| 1 |
| 3 |
http://sqlfiddle.com/#!18/85c9c/5
Try:
select * from UserDetail u,TimeTable t
where u.userid=t.userid
and
(select COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'tblName'
AND TABLE_CATALOG = 'DBNAME'
AND COLUMN_NAME = CONCAT(SUBSTRING(CONVERT(VARCHAR, getdate(), 100), 13, 2) ,'',RIGHT(CONVERT(VARCHAR, GETDATE(), 100),2))) = 1
select CONCAT(SUBSTRING(CONVERT(VARCHAR, getdate(), 100), 13, 2) ,'',RIGHT(CONVERT(VARCHAR, GETDATE(), 100),2)) is a value of type varchar, not the syntax of a query. What you are currently doing is for sql similar to:
select * from UserDetail u,TimeTable t
where u.userid=t.userid
and '12PM' = 1
Even if parser accepted your query the condition would always be false as '12PM' is not the same as 12PM or [12PM]. You cannot dynamically modify a query within the query itself to make it work.
Yes, you could do tricks like dynamic execution (build entire query as string and then execute it), but please -don't. The real problem in you code is that you have a rather bad table design. Consider redesigning your TimeTable table to have timeOfDay values a single column and actual time values as data in that column. You'll save yourself a lot of headache later.

SQL Server SUM based on subsequent records

Microsoft SQL Server 2012 (SP1) - 11.0.3156.0 (X64)
I am not sure of the best way to word this and have tried a few different searches with different combinations of words without success.
I only want to Sum Sequence = 1 when there are Sequence > 1, in the table below the Sequence = 1 lines marked with *. I don't care at all about checking that Sequence 2,3,etc match the same pattern because if they exist at all I need to Sum them.
I have data that looks like this:
| Sequence | ID | Num | OtherID |
|----------|----|-----|---------|
| 1 | 1 | 10 | 1 |*
| 2 | 1 | 15 | 1 |
| 3 | 1 | 20 | 1 |
| 1 | 2 | 10 | 1 |*
| 2 | 2 | 15 | 1 |
| 1 | 3 | 10 | 1 |
| 1 | 1 | 40 | 3 |
I need to sum the Num column but only when there is more than one sequence. My output would look like this:
Sequence Sum OtherID
1 20 1
2 30 1
3 20 1
I have tried grouping the queries in a bunch of different ways but really by the time I get to the sum, I don't know how to look ahead to make sure there are greater than 1 sequences for an ID.
My query at the moment looks something like this:
select Sequence, Sum(Num) as [Sum], OtherID
from tbl
where ID in (Select ID from tbl where Sequence > 1)
Group by Sequence, OtherID
tbl is a CTE that I wrapped around my query and it partially works, but is not really the filter I wanted.
If this is something that just shouldn't be done or can't be done then I can handle that, but if it's something I am missing I'd like to fix the query.
Edit:
I can't give the full query here but I started with this table/data (to get the above output). The OtherID is there because the data has the same ID/Sequence combinations but that OtherID helps separate them out so the rows are not identical (multiple questions on a form).
Create table #tmpTable (ID int, Sequence int, Num int, OtherID int)
insert into #tmpTable (ID, Sequence, Num, OtherID) values (1, 1, 10, 1)
insert into #tmpTable (ID, Sequence, Num, OtherID) values (1, 2, 15, 1)
insert into #tmpTable (ID, Sequence, Num, OtherID) values (1, 3, 20, 1)
insert into #tmpTable (ID, Sequence, Num, OtherID) values (2, 1, 10, 1)
insert into #tmpTable (ID, Sequence, Num, OtherID) values (2, 2, 15, 1)
insert into #tmpTable (ID, Sequence, Num, OtherID) values (3, 1, 10, 1)
insert into #tmpTable (ID, Sequence, Num, OtherID) values (1, 1, 40, 3)
The following will sum over Sequence and OtherID, but only when:
Either
sequence is greater than 1
or
there is something else with the same ID and OtherID, but a different sequence.
Query:
select Sequence, Sum(Num) as SumNum, OtherID from #tmpTable a
where Sequence > 1
or exists (select * from #tmpTable b
where a.ID = b.ID
and a.OtherID = b.OtherID
and b.Sequence <> a.Sequence)
group by Sequence, OtherID;
It looks like you are trying to sum by Sequence and OtherID if the Count of ID >1, so you could do something like below:
select Sequence, Sum(Num) as [Sum], OtherID
from tbl
where ID in (Select ID from tbl where Sequence > 1)
Group by Sequence, OtherID
Having count(id)>1

Resources