doubled and tripled sql server results - sql-server

I want to create a specific table but some numeric values are doubled or tripled in the result.
here is the situation:
2 tables: Payments and Expenses
[Payments]: ID, studentID, Amount, DOP (a row in this table is a payment which a student pays it on DOP (date).
[Expenses]: ID, AmountPaid, TimeStamp (a row in this table is an expense bought such as papers or pens... on a specific date(timestamp)
my query is:
select
sum(purchases.amount) as 'Income From Students',
sum(Expenses.amountpaid) as 'Expenses',
sum(purchases.amount-expenses.amountpaid) as 'Net Profit',
datename(month,timestamp) as 'Month',
datepart(year,timestamp) as 'Year'
from expenses,purchases
group by datename(month,timestamp),datepart(year,timestamp)
as the query tells: my table should display for each month and each year the sum of payments, expenses and net profit=payments - expenses.
the problem is, when getting the result, sum(expenses.amountpaid) is always doubled.
so any ideas...

Sounds like you need to specify the relationship between the two tables.
Something like this, I assume:
select
sum(purchases.amount) as 'Income From Students',
sum(Expenses.amountpaid) as 'Expenses',
sum(purchases.amount-expenses.amountpaid) as 'Net Profit',
datename(month,timestamp) as 'Month',
datepart(year,timestamp) as 'Year'
from expenses,purchases
WHERE PURCHASES.DOP = EXPENSES.TIMESTAMP /*Add this*/
group by datename(month,timestamp),datepart(year,timestamp)

SELECT T.INCOME,T.EXPENSE,SUM(T.INCOME)-SUM(T.EXPENSE) AS PROFIT
FROM (SELECT SUM(P.amount) AS Income, SUM(E.amountpaid) AS Expense
FROM Payments P,Expenses E WHERE P.ID=E.ID
GROUP BY datename(month, timestamp), datepart(year, timestamp)) AS T;

I Solved it Guys, After 4 hours of Trying, the query is:
select sum(P.Income) as 'Income from Payments',
sum(E.expense) as 'Expenses',
sum(P.Income)-sum(E.expense) as 'Net Profit',
DateName( month , DateAdd( month , IncomeMonth , 0 ) - 1 ) as 'Month'
from
(select sum(payments.amountpaid) as Income,
month(DOP) as IncomeMonth
from payments group by month(dop)) as P,
(select sum(expenses.amountpaid) as Expense,
month(timestamp) as ExpenseMonth
from expenses
group by month(timestamp))
as E
where
E.Expensemonth=P.IncomeMonth
group by P.IncomeMonth

Related

postgresql count number of employees per year

Columns: id, first_name, last_name, start_date, end_date
NULL allowed only in end-date.
This works for showing how many people started in a particular year:
SELECT
to_char(date_trunc('year', start_date)::date, 'YYYY') AS "Year",
COUNT(*) AS "New Employees"
FROM employees
GROUP BY date_trunc('year', start_date)
ORDER BY date_trunc('year', start_date) ASC
I cannot find a solution to display a list of years with the total number of employees in a particular year.
The assumption is that e.g. in 2022 I would count those with end_date = null AND end_date = 2022.
You need a list of all relevant years, that is to say, all years from the earliest starting date until the current year. Then count the number of active employees for each year.
PostgreSQL provides the useful generate_series function for creating the range of years. You can use coalesce to replace an empty end_date with the current date, available from current_date. The resulting query is
with years(year) as (
select generate_series(
date_trunc('year', (select min(start_date) from employees)),
date_trunc('year', current_date),
interval '1 year'
)
)
select to_char(year, 'yyyy') as year, count(*)
from years
join employees on start_date < year + '1 year'
and coalesce(end_date, current_date) >= year
group by 1
order by 1;

How can I add condition for total sales per month for specific category

Let's assume there is a table as below structure
select Year, Month , customer, Category, Amount
from claim
I want to apply a discount for each month as shown in the table (green columns):
if the 'Cus X' total sales for brand and generic categories in JAN is greater than 15000, then apply a discount for only 'Brand' category of 2%
if it is greater than 20000 then apply a discount of 3%
and the same thing for other months and customers.
Is it possible to do that in SQL with a subquery or some functions that can help?
Please advise
You can use case .. when statement and analytical function as follows:
select Year, Month , customer, Category, Amount,
Case when category = 'Brand'
then
Case when total_sales > 15000 then '2%'
When total_sales > 20000 then '3%'
End
End as disc,
Case when category = 'Brand'
then
Case when total_sales > 15000 then 2*amount/100
When total_sales > 20000 then 3*amount/100
End
End disc_amount
From
(select Year, Month , customer, Category, Amount,
sum(case when category in ('Brand', 'Generic') then amount else 0 end)
over (partition by year, month, customer) as total_sales
from claim)

Query for generating month level & YTD level data

Write SQL Queries to -
Given - Day level Sales Data for different Companies
1) Create Month Level , YTD Level Data for the given data.
YTD- Year to Date
YTD(Mar) = Jan + Feb +Mar
2) Create Overall level on the basis of Company for the data created in Step 1. Mark it as "Industry"
Eg: Industry = CompA + CompB + CompC + CompD
3) Calculate Month and YTD Level Share( both Value , Volume) for the data created after Step 2.
Calculation of Share - Comp / Industry.
I get that we can use partition by in over clause but in general i don't understand the question.
schema:
[Period] - date
[Company]- nvarchar
[Metric] - nvarchar
[Values] - Float
Period Company Metric Values
01-01-2018 CompA Sales Vol 72947.30664
02-01-2018 CompA Sales Vol 21553.65941
03-01-2018 CompA Sales Vol 777.6831962
04-01-2018 CompA Sales Vol 34871.11234
05-01-2018 CompA Sales Vol 42598.06526
I tried using partition by month & year. but I'm not clear if this is what is expected.
SELECT YEAR([Period]) AS Year,
MONTH([Period]) as month,
MTD = SUM([Values]) OVER (PARTITION BY year([period]),month([period]) order by period),
YTD = SUM([Values]) OVER (PARTITION BY Year([period]) order by period)
FROM DP
From the sounds of the question it seems like what is being asked is the expression of layers of totals.
Your query works well and does everything required except for the total for the industry.
The query below uses "rollup" which allows you to generate hierarchical groupings based on the columns running left to right. As such you will get a grand total for all industries, a total for all years per company, and a total for all months per year per company.
declare #sales table
(
[Period] date,
[Company] nvarchar(50),
[Metric] nvarchar(50),
[Values] float
);
insert #sales ([Period], [Company], [Metric], [Values])
values
('01-01-2018', 'CompA', 'Sales Vol', 72947.30664),
('02-01-2018', 'CompA', 'Sales Vol', 21553.65941),
('03-01-2018', 'CompA', 'Sales Vol', 777.6831962),
('04-01-2018', 'CompA', 'Sales Vol', 34871.11234),
('05-01-2018', 'CompA', 'Sales Vol', 42598.06526);
SELECT coalesce(Company,'Industry') as Company, coalesce(cast(YEAR([Period]) as nvarchar(50)), 'All Years') AS Year,
coalesce(cast(MONTH([Period])as nvarchar(50)),'All Months') as month, coalesce(sum([values]),0) as sales
FROM #sales
group by rollup (company, year([period]), month([period]))

SQL- Calculating average of differences between times

I have an sql table that has transaction history of all the clients. I want to find what is the average difference in time between two transactions.
ClientCode Date
DL2xxx 2016-04-18 00:00:00.000
DL2xxx 2016-04-18 00:00:00.000
E19xxx 2016-04-18 00:00:00.000
E19xxx 2016-04-18 00:00:00.000
E19xxx 2016-04-18 00:00:00.000
JDZxxx 2016-04-18 00:00:00.000
Given above are the first few lines of the table the date given is the date transaction happened. I want to take an average of difference in days when successive transactions happen. Say for a client he makes transactions of Day 1, Day 3, Day 10, and Day 15. So differences are {2, 7, 5} average of which is 4.66. If only one transaction takes place this should be 0.
ClientCode AverageDays
DL2xxx <float_value>
DL2xxx <float_value>
E19xxx <float_value>
This is what the output should look like where each unique client code occurs only once.
You can use a query like below if you table name is T
see live demo
select
ClientCode,
AvgDays =ISNULL(AVG(d),0)
from
(
select
*,
d=DATEDIFF(
d,
dateofT,
LEAD(DateofT) over(
partition by ClientCode
order by DateofT asc ))
from t
)t
group by ClientCode
If Windowing functions aren't available to you, here's an alternative
--CREATE SAMPLE DATA
CREATE TABLE #TMP(ClientID INT, EventDate DATE)
GO
INSERT INTO #TMP VALUES
(1,DATEADD(DD,RAND()*365,'20180101'))
,(2,DATEADD(DD,RAND()*365,'20180101'))
,(3,DATEADD(DD,RAND()*365,'20180101'))
,(4,DATEADD(DD,RAND()*365,'20180101'))
,(5,DATEADD(DD,RAND()*365,'20180101'))
GO 50
--PRE SQL 2012 Compatible
SELECT A.ClientID
,AVG(DATEDIFF(DD,C.EventDate,A.Eventdate)) AS ClientAvg
FROM #TMP A
CROSS APPLY (SELECT ClientID, MAX(EventDate) EventDate FROM #TMP B
WHERE A.ClientID = B.ClientID AND A.EventDate > B.EventDate
GROUP BY ClientID) C
GROUP BY A.ClientID
ORDER BY A.ClientID
You can use LAG() function to compare a date to it's previous date by client, then group by client and calculate the average.
IF OBJECT_ID('tempdb..#Transactions') IS NOT NULL
DROP TABLE #Transactions
CREATE TABLE #Transactions (
ClientCode VARCHAR(100),
Date DATE)
INSERT INTO #Transactions (
ClientCode,
Date)
VALUES
('DL2', '2016-04-18'),
('DL2', '2016-04-19'),
('DL2', '2016-04-26'),
('E19', '2016-01-01'),
('E19', '2016-01-11'),
('E19', '2016-01-12')
;WITH DayDifferences AS
(
SELECT
T.ClientCode,
T.Date,
DayDifference = DATEDIFF(
DAY,
LAG(T.Date) OVER (PARTITION BY T.ClientCode ORDER BY T.Date ASC),
T.Date)
FROM
#Transactions AS T
)
SELECT
D.ClientCode,
AverageDayDifference = AVG(ISNULL(CONVERT(FLOAT, D.DayDifference), 0))
FROM
DayDifferences AS D
GROUP BY
D.ClientCode
Using the observation that the sum of differences within a group is simply the max - min of that group, you can use the simple group by select:
select IIF(COUNT(*) > 1,
(CAST(DATEDIFF(day, MIN(DateofT), MAX(DateofT)) AS FLOAT)) / (COUNT(*) - 1), 0.0)
AS AVGDays, ClientCode
FROM t GROUP BY ClientCode

SQL- Finding a gap that is x amount of months with the same foreign key

I am editing this to clarify my question.
Let's say I have a table that holds patient information. I need to find new patients for this year, and the date of their prescription first prescription when they were considered new. Anytime there is a six month gap they are considered a new patient.
How do I accomplish this using SQL. I can do this in Java and any other imperative language easily enough, but I am having problems doing this in SQL. I need this script to be run in Crystal by non-SQL users
Table:
Patient ID Prescription Date
-----------------------------------------
1 12/31/16
1 03/13/17
2 10/10/16
2 05/11/17
2 06/11/17
3 01/01/17
3 04/20/17
4 01/31/16
4 01/01/17
4 07/02/17
So Patients 2 and 4 are considered new patients. Patient 4 is considered a new patient twice, so I need dates for each time patient 4 was considered new 1/1/17 and 7/2/17. Patients 1 and 3 are not considered new this year.
So far I have the code below which tells me if they are new this year, but not if they had another six month gap this year.
SELECT DISTINCT
this_year.patient_id
,this_year.date
FROM (SELECT
patient_id
,MIN(prescription_date) as date
FROM table
WHERE prescription_date BETWEEN '2017-01-01 00:00:00.000' AND '2017-
12-31 00:00:00.000'
GROUP BY [patient_id]) AS this_year
LEFT JOIN (SELECT
patient_id
,MAX(prescription_date) as date
FROM table
WHERE prescription_date BETWEEN '2016-01-01 00:00:00.000' AND '2016-
12-31 00:00:00.000'
GROUP BY [patient_id]) AS last_year
WHERE DATEDIFF(month, last_year.date, this_year.date) > 6
OR last_year.date IS NULL
Patient 2 in your example does not meet the criteria you specified ... that being said ...
You can try something like this ... untested but should be similar (assuming you can put this in a stored procedure):
WITH ordered AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY [Prescription Date]) rn
FROM table1
)
SELECT o1.[PatientID], DATEDIFF(s, o1.[Prescription Date], o2.[Prescription Date]) diff
FROM ordered o1 JOIN ordered o2
ON o1.rn + 1 = o2.rn
WHERE DATEDIFF(m, o1.[Prescription Date], o2.[Prescription Date]) > 6
Replace table1 with the name of your table.
I assume that you mean the patient has not been prescribed in the last 6 months.
SELECT DISTINCT user_id
FROM table_name
WHERE prescribed_date >= DATEADD(month, -6, GETDATE())
This gives you the list of users that have been prescribed in the last 6 months. You want the list of users that are not in this list.
SELECT DISTINCT user_id
FROM table_name
WHERE user_id NOT IN (SELECT DISTINCT user_id
FROM table_name
WHERE prescribed_date >= DATEADD(month, -6, GETDATE()))
You'll need to amend the field and table names.

Resources