I have a Microsoft SQL Server with the following tables:
Projects
BookedHours (with fk_Project = Projects.ID)
Products
ProjectsToProducts (n:m with fk_Projects = Projects.ID and fk_Products = Products.ID)
I now want to select how many hours are booked to which product per month. The problem is, that one project can have multiple products (that's why I need the n:m table).
If I do the following, it will count the hours twice if a project has two products.
SELECT
P.ID AS fk_Product, MONTH(B.Datum) AS Monat, SUM(B.Hours) AS Stunden
FROM
tbl_BookedHours AS B
INNER JOIN
tbl_Projects AS M on B.fk_Project = M.ID
INNER JOIN
tbl_ProjectProduct AS PP ON PP.fk_Project = M.ID
INNER JOIN
tbl_Products AS P ON PP.fk_Product = P.ID
WHERE
YEAR(B.Datum) = 2020
GROUP BY
P.ID, MONTH(B.Datum)
ORDER BY
P.ID, MONTH(B.Datum)
I can get the number of products for each project with this SQL:
SELECT fk_Project, COUNT(*) AS Cnt
FROM tbl_ProjectProduct
GROUP By fk_MainProject
But how can I now divide the hours for each project by its individual factor and add it all up per product and month?
I could do it in my C# program or I could use a cursor and iterate through all projects, but I think there should be an more elegant way.
Edit with sample data:
|----------------| |----------------| |------------------------------|
| tbl_Projects | | tbl_Products | | tbl_ProjectProduct |
|----------------| |----------------| |------------------------------|
| ID | Name | | ID | Name | | ID | fk_Project | fk_Product |
|----+-----------| |----+-----------| |------------------------------|
| 1 | Project 1 | | 1 | Product 1 | | 1 | 1 | 1 |
| 2 | Project 2 | | 2 | Product 2 | | 2 | 1 | 2 |
| 3 | Project 3 | | 3 | Product 3 | | 3 | 2 | 1 |
| 4 | Project 4 | | 4 | Product 4 | | 4 | 3 | 3 |
|----------------| |----------------| | 5 | 4 | 1 |
| 6 | 4 | 2 |
| 7 | 4 | 4 |
|------------------------------|
|--------------------------------------|
| tbl_BookedHours |
|--------------------------------------|
| ID | fk_Project | Hours | Date |
|--------------------------------------|
| 1 | 1 | 10 | 2020-01-15 |
| 2 | 1 | 20 | 2020-01-20 |
| 3 | 2 | 10 | 2020-01-15 |
| 4 | 3 | 30 | 2020-01-18 |
| 5 | 2 | 20 | 2020-01-20 |
| 6 | 4 | 30 | 2020-01-25 |
| 7 | 1 | 10 | 2020-02-15 |
| 8 | 1 | 20 | 2020-02-20 |
| 9 | 2 | 10 | 2020-02-15 |
| 10 | 3 | 30 | 2020-03-18 |
| 11 | 2 | 20 | 2020-03-20 |
| 12 | 4 | 30 | 2020-03-25 |
|--------------------------------------|
The Result should be:
|----------------------------|
| fk_Product | Month | Hours |
|----------------------------|
| 1 | 1 | 55 |
| 2 | 1 | 25 |
| 3 | 1 | 30 |
| 4 | 1 | 10 |
| 1 | 2 | 25 |
| 2 | 2 | 15 |
| 1 | 3 | 30 |
| 2 | 3 | 10 |
| 3 | 3 | 30 |
| 4 | 3 | 10 |
|----------------------------|
For example booking Nr. 1 has to be divided by 2 (because Project 1 has two products) and one half of amount added to Product 1 and the other to Product 2 (Both in January). Booking Nr. 4 should not be divided, because Project 3 only has one product. Booking Numer 12 for example has to be divided by 3.
So that in total the Hours in the end add up to the same total.
I hope it's clearer now.
*** EDIT 2***
DECLARE #tbl_Projects TABLE (ID INT, [Name] VARCHAR(MAX))
INSERT INTO #tbl_Projects VALUES
(1,'Project 1'),
(2,'Project 2'),
(3,'Project 3'),
(4,'Project 4')
DECLARE #tbl_Products TABLE (ID INT, [Name] VARCHAR(MAX))
INSERT INTO #tbl_Products VALUES
(1,'Product 1'),
(2,'Product 2'),
(3,'Product 3'),
(4,'Product 4')
DECLARE #tbl_ProjectProduct TABLE (ID INT, fk_Project int, fk_Product int)
INSERT INTO #tbl_ProjectProduct VALUES
(1,1,1),
(2,1,2),
(3,2,1),
(4,3,3),
(5,4,1),
(6,4,2),
(7,4,4)
DECLARE #tbl_BookedHours TABLE (ID INT, fk_Project int, Hours int, [Date] Date)
INSERT INTO #tbl_BookedHours VALUES
(1,1,10,'2020-01-15'),
(2,1,20,'2020-01-20'),
(3,2,10,'2020-01-15'),
(4,3,30,'2020-01-18'),
(5,2,20,'2020-01-20'),
(6,4,30,'2020-01-25'),
(7,1,10,'2020-02-15'),
(8,1,20,'2020-02-20'),
(9,2,10,'2020-02-15'),
(10,3,30,'2020-03-18'),
(11,2,20,'2020-03-20'),
(12,4,30,'2020-03-25')
SELECT P.ID AS fk_Product, MONTH(B.Date) AS Month, SUM(B.Hours) AS SumHours
FROM #tbl_BookedHours AS B INNER JOIN #tbl_Projects AS M on B.fk_Project = M.ID
INNER JOIN #tbl_ProjectProduct AS PP ON PP.fk_Project = M.ID
INNER JOIN #tbl_Products AS P ON PP.fk_Product = P.ID
GROUP BY P.ID,MONTH(B.Date)
ORDER BY P.ID, MONTH(B.Date)
This gives me the wrong result, because it Counts the hours for both products:
| fk_Product | Month | SumHours |
|-------------------------------|
| 1 | 1 | 90 |
| 1 | 2 | 40 |
| 1 | 3 | 50 |
| 2 | 1 | 60 |
| 2 | 2 | 30 |
| 2 | 3 | 30 |
| 3 | 1 | 30 |
| 3 | 3 | 30 |
| 4 | 1 | 30 |
| 4 | 3 | 30 |
|-------------------------------|
Consider the following query. I modified your table variables to temp tables so it was easier to debug.
;WITH CTE AS
(
SELECT fk_Project, count(fk_Product) CNT
FROM #tbl_ProjectProduct
GROUP BY fk_Project
)
,CTE2 AS
(
SELECT t1.Date, t2.fk_Project, Hours/CNT NewHours
FROM #tbl_BookedHours t1
INNER JOIN CTE t2 on t1.fk_Project = t2.fk_Project
)
SELECT t4.ID fk_Product, MONTH(date) MN, SUM(NewHours) HRS
FROM CTE2 t1
INNER JOIN #tbl_Projects t2 on t1.fk_Project = t2.id
INNER JOIN #tbl_ProjectProduct t3 on t3.fk_Project = t2.ID
INNER JOIN #tbl_Products t4 on t4.ID = t3.fk_Product
GROUP BY t4.ID,MONTH(date)
I am not sure why my query isn't returning the right values when I join a temp table with an actual table. I am using SQL Server 2008 as my database.
I have the below query
WITH TradeHistory AS (
SELECT
tran_num,
version_number,
row_creation AS last_update,
tran_status,
ROW_NUMBER() OVER(PARTITION BY tran_num ORDER BY tran_num DESC, row_creation DESC, version_number DESC) AS RowNum
FROM tran_history_view (NOLOCK)
where
row_creation BETWEEN '2018-01-22 17:02:47.083' AND '2019-01-23 19:02:47.083'
AND tran_status IN (3,5)
AND update_type NOT IN (15,52,24)
AND deal_tracking_num= 10738416
)
select ab.tran_num as ab_tran_num, ab.version_number as ab_version_num, hist.version_number as hist_version_num, ab.deal_tracking_num as ab_track_num,
ab.tran_status as ab_tran_status, hist.tran_status as hist_tran_status from
TradeHistory hist join ab_tran ab on ab.tran_num = hist.tran_num
and hist.RowNum =1
I am not sure why my join does not pick the matching row for tran_num and version_num? I get a different version_number and tran_status from tran_history table. If I join on version_number across the two tables, I don't get any rows (as expected based on the values in the final output).
My 'TradeHistory' temp table returns the below values
+----------+----------------+-------------------------+-------------+--------+
| tran_num | version_number | last_update | tran_status | RowNum |
+----------+----------------+-------------------------+-------------+--------+
| 10738416 | 2 | 2019-01-23 16:02:09.760 | 3 | 1 |
| 10738416 | 1 | 2019-01-23 16:01:51.803 | 3 | 2 |
| 10738422 | 4 | 2019-01-23 16:02:30.600 | 3 | 1 |
| 10738422 | 3 | 2019-01-23 16:02:30.243 | 3 | 2 |
| 10738422 | 2 | 2019-01-23 16:02:09.973 | 3 | 3 |
+----------+----------------+-------------------------+-------------+--------+
Result of output joining the select query with the temp table is as below. The version_number and tran_number from the two tables are different and I don't understand why? Can someone please explain?
+---------------+-------------+----------------+--------------+--------------+----------------+------------------+
| hist_tran_num | ab_tran_num | ab_version_num | hist_ver_num | ab_track_num | ab_tran_status | hist_tran_status |
+---------------+-------------+----------------+--------------+--------------+----------------+------------------+
| 10738416 | 10738416 | 4 | 2 | 10738416 | 10 | 3 |
| 10738422 | 10738422 | 6 | 4 | 10738416 | 10 | 3 |
+---------------+-------------+----------------+--------------+--------------+----------------+------------------+
Let's say I have a historical table keeping who has modified data
-------------------------------------------------------------
| ID | Last_Modif | User_Modif | Col3, Col4...
-------------------------------------------------------------
| 1 | 2018-04-09 12:12:00 | John
| 2 | 2018-04-09 11:10:00 | Jim
| 3 | 2018-04-09 11:05:00 | Mary
| 4 | 2018-04-09 11:00:00 | John
| 5 | 2018-04-09 10:56:00 | David
| 6 | 2018-04-09 10:53:00 | John
| 7 | 2018-04-08 19:50:00 | Eric
| 8 | 2018-04-08 18:50:00 | Chris
| 9 | 2018-04-08 15:50:00 | John
| 10 | 2018-04-08 12:50:00 | Chris
----------------------------------------------------------
I would like to find the modifs done by John and previous version before he did that, to check what he had modified. For example in this scenario I would like to return row 1,2,4,5,6,7,9,10
I am thinking of ranking first based on Last_modif then do a join to pick up the next row, but somehow the result is not correct. This seems not a LAG/LEAD case since I am not picking a single value from the next row, but instead the whole next row. Any idea ?
-- sample 1000 rows with RowNumber
with TopRows as
(select top 1000 *, ROW_NUMBER() OVER(ORDER BY Last_modif desc) RowNum from [Table])
--Reference rows : Rows modif by John
, ModifByJohn as
(Select * from TopRows where USER_MODIF = 'John')
select * from ModifByJohn
UNION
select ModifByNext.* from ModifByJohn join TopRows ModifbyNext on ModifByJohn.RowNum + 1 = ModifByNext.RowNum
order by RowNum
How will the code look like if we would like to return last 2 modifs before John did instead of 1 ?
Maybe you can take advantage of your current ID:
with x as
(
select t1.*,
(select top 1 id from tbl where id > t1.id) prev_id
from tbl t1
where t1.User_Modif = 'John'
)
select * from x;
GO
ID | Last_Modif | User_Modif | prev_id
-: | :------------------ | :--------- | ------:
1 | 09/04/2018 12:12:00 | John | 2
4 | 09/04/2018 11:00:00 | John | 5
6 | 09/04/2018 10:53:00 | John | 7
9 | 08/04/2018 15:50:00 | John | 10
with x as
(
select t1.*,
(select top 1 id from tbl where id > t1.id) prev_id
from tbl t1
where t1.User_Modif = 'John'
)
select ID, Last_Modif, User_Modif from x
union all
select ID, Last_Modif, User_Modif
from tbl
where ID in (select prev_id from x)
order by ID
GO
ID | Last_Modif | User_Modif
-: | :------------------ | :---------
1 | 09/04/2018 12:12:00 | John
2 | 09/04/2018 11:10:00 | Jim
4 | 09/04/2018 11:00:00 | John
5 | 09/04/2018 10:56:00 | David
6 | 09/04/2018 10:53:00 | John
7 | 08/04/2018 19:50:00 | Eric
9 | 08/04/2018 15:50:00 | John
10 | 08/04/2018 12:50:00 | Chris
dbfiddle here
I have two tables as follows:
Table1:
ID | FName | LName
1 | A1 | A2
2 | B1 | B2
3 | C1 | C2
Table2:
ID | Price | Month | T1ID
1 | 5 | 1 | 1
2 | 5 | 1 | 2
3 | 5 | 2 | 3
Result:
Where Month = '1'
ID | FName | LName | Price | Month | T1ID
1 | A1 | A2 | 5 | 1 | 1
2 | B1 | B2 | 5 | 1 | 2
NULL| C1 | C2 | NULL | NULL | 3
Where Month = '2'
ID | FName | LName | Price | Month | T1ID
NULL| A1 | A2 | NULL | NULL | 1
NULL| B1 | B2 | NULL | NULL | 2
3 | C1 | C2 | 5 | 2 | 3
Looks like a left join with multiple join conditions:
SELECT t2.ID, t1.FName, t1.LName, t2.Price, t2.Month, t1.ID
FROM Table1 t1
LEFT JOIN Table2 t2 ON t1.ID = t2.T1ID AND t2.Month = '1' -- or '2'
The trick is to do filtering by adding additional condition to ON clause, which helps to filter records during the join. If you used the condition in WHERE clause, some records would be filtered out from the results, which is not what you expect.
Hi friends i have small doubt in sql server
here i want data based on condition
same id and status is equal to s then that date value be
how to write query in sql server
Table :emp
id |status |date(mm-dd-yy) |fdate(mm-dd-yy) |tdate(mm-dd-yy)
1 | S |03-16-11 | |
1 | b | | 03-15-11 |03-18-11
1 | s |03-17-11 | |
1 | b | | 04-20-12 |04-30-12
1 | S |04-20-12 | |
1 | s |04-10-12 | |
1 | s |10-01-14 | |
1 | b | |10-02-14 |10-25-14
2 | s |01-18-12 | |
2 | b | |01-18-12 |01-28-12
2 | b | |03-10-13 |03-24-13
2 | s |03-16-13 | |
2 | s |03-10-13 | |
2 | s |03-23-13 | |
2 | b | |04-20-13 |04-27-13
2 | s |07-01-14 | |
the table (status = s, id, date) compare it with status = b, same id number and date ( Date value from status s) with the date range of fdate and tdate .
if that data with in range then Billing yes other wise billing no
output like
id |status |date(mm-dd-yy) |fdate(mm-dd-yy) |tdate(mm-dd-yy) |Billing
1 | S |03-16-11 | | |yes
1 | s |03-17-11 | | |yes
1 | S |04-20-12 | | |yes
1 | s |04-10-12 | | |no
1 | s |10-01-14 | | |no
2 | s |01-18-12 | | |yes
2 | s |03-16-13 | | |yes
2 | s |03-10-13 | | |yes
2 | s |03-23-13 | | |yes
2 | s |07-01-14 | | |no
i tried query like below
select *
from ( select * from emp a where status ='s') a
inner join (select * from emp b where status='b') b
on a.pn=b.pn
where a.date<=b.date1 and a.date>=b.date2
its not give exactely result.
please tell me how to write query in sql server .
Try
select a.Id,
a.status,
a.date,
a.fdate,
a.tdate,
max(IsNull(case when a.date between b.fDate and b.tDate
then 'yes'
else 'no'
end, 'no')) Billing
from emp a
left join emp b
on a.Id=b.Id
where a.status ='s'
and b.status = 'b'
group by a.Id,
a.status,
a.date,
a.fdate,
a.tdate
Some questions/comments:
What are the fields: pn, date1 and date2?
date1 in your query is, I guess, bigger than date2