Join two tables to generate a graph, but they don't have common columns - sql

I'm trying to build a SQL consult that populates a data table that will give the info that will draw a js graph (on canvasjs library).
The graph will compare the sales table vs. the expenses table, so they don't have a column on wich I can make a JOIN, only the dates, that will be shown as yyyy-mm.
In order to group all the sales, I need to Join two tables, the one that stores the main invoices info ([dbo].[Facturas]), and the one that stores the items on each invoice ([dbo].[FacturasItems])
This is the consult that retrieves the info for all the sales, and groups them in months:
DECLARE #facTotal numeric(18,2)
SET #facTotal =
(
SELECT
SUM(([dbo].[FacturasItems].[ValorU] * [dbo].[FacturasItems].[Cantidad]) + (([dbo].[FacturasItems].[ValorU] * [dbo].[FacturasItems].[Cantidad]) * [dbo].[Facturas].[ValorIVA]))
FROM [dbo].[FacturasItems]
INNER JOIN
[dbo].[Facturas]
ON [dbo].[Facturas].[ID] = [dbo].[FacturasItems].[IdFactura]
)
SELECT
CAST(YEAR([dbo].[Facturas].[FechaCr]) AS VARCHAR(4)) + '-' + right('00' + CAST(MONTH([dbo].[Facturas].[FechaCr]) AS VARCHAR(2)), 2) AS [Periodo],
CONVERT(numeric(18,0), SUM(([dbo].[FacturasItems].[ValorU] * [dbo].[FacturasItems].[Cantidad]) + (([dbo].[FacturasItems].[ValorU] * [dbo].[FacturasItems].[Cantidad]) * [dbo].[Facturas].[ValorIVA]))) AS [VentasRaw],
'$' + CONVERT(varchar, CONVERT(money, SUM(([dbo].[FacturasItems].[ValorU] * [dbo].[FacturasItems].[Cantidad]) + (([dbo].[FacturasItems].[ValorU] * [dbo].[FacturasItems].[Cantidad]) * [dbo].[Facturas].[ValorIVA]))), 1) AS [VentasForm],
CONVERT(varchar(6), CONVERT(numeric(18,2), SUM(([dbo].[FacturasItems].[ValorU] * [dbo].[FacturasItems].[Cantidad]) + (([dbo].[FacturasItems].[ValorU] * [dbo].[FacturasItems].[Cantidad]) * [dbo].[Facturas].[ValorIVA])) / (#facTotal) * 100)) + '%' AS [Prc100]
FROM [dbo].[Facturas]
INNER JOIN
[dbo].[FacturasItems]
ON [dbo].[FacturasItems].[IdFactura] = [dbo].[Facturas].[ID]
GROUP BY
CAST(YEAR([dbo].[Facturas].[FechaCr]) AS VARCHAR(4)) + '-' + right('00' + CAST(MONTH([dbo].[Facturas].[FechaCr]) AS VARCHAR(2)), 2)
This consult is working just fine, and it returns this table data:
The second consult, expenses, goes like this:
DECLARE #gasTotal numeric(18,2)
SET #gasTotal =
(
SELECT
SUM(([dbo].[Gastos].[Valor]) + (([dbo].[Gastos].[Valor]) * [dbo].[Gastos].[IVAVal]))
FROM [dbo].[Gastos]
)
SELECT
CAST(YEAR([dbo].[Gastos].[Fecha]) AS varchar(4)) + '-' + RIGHT('00' + CAST(MONTH([dbo].[Gastos].[Fecha]) AS varchar(2)), 2) AS [Periodo],
CONVERT(numeric(18,0), SUM([dbo].[Gastos].[Valor] + ([dbo].[Gastos].[Valor] * [dbo].[Gastos].[IVAVal]))) AS [ValorGraph],
'$' + CONVERT(varchar, CONVERT(money, SUM([dbo].[Gastos].[Valor] + ([dbo].[Gastos].[Valor] * [dbo].[Gastos].[IVAVal]))), 1) AS [ValorForm],
CONVERT(varchar, CONVERT(money, SUM(([dbo].[Gastos].[Valor] + ([dbo].[Gastos].[Valor] * [dbo].[Gastos].[IVAVal])) / #gasTotal) * 100), 1) + '%' AS [Prc100]
FROM [dbo].[Gastos]
GROUP BY
CAST(YEAR([dbo].[Gastos].[Fecha]) AS varchar(4)) + '-' + RIGHT('00' + CAST(MONTH([dbo].[Gastos].[Fecha]) AS varchar(2)), 2)
This will create this table:
So, as you can see, there where no sales on 2017-10, zero invoices, so the table 1 won't start on 2017-10 but on 2017-11, while the expenses table starts on 2017-10.
I need that the table 1 (invoices and sales), starts in 2017-10 with it's values in 0, so that the graph will show correcty.
In other words, I need this kind of tables:
I tryed an inner join, a left join and an outer join, but the will add all the records, add the sales + expenses.
Thanks!

Just add
SELECT 2017-10 as Periodo,0 as VentasRow, 0 as VentasForm, 0 as Prc100
UNION
Your first query here....
Hope this helps.

WITH Ventas as (
SELECT
CAST(YEAR(f.FechaCr) AS VARCHAR(4)) + '-'
+ right('00' + CAST(MONTH(f.FechaCr) AS VARCHAR(2)), 2) AS Periodo,
SUM(fi.ValorU * fi.Cantidad * (1 + f.ValorIVA)) AS VentasRaw,
SUM(fi.ValorU * fi.Cantidad * (1 + f.ValorIVA)) OVER () AS VentasTot
FROM dbo.Facturas as f INNER JOIN dbo.FacturasItems as fi
ON fi.IdFactura = f.ID
GROUP BY datepart(year, f.FechaCr), datepart(month, f.FechaCr)
),
Gastos as (
SELECT
CAST(YEAR(g.Fecha) AS varchar(4)) + '-'
+ RIGHT('00' + CAST(MONTH(g.Fecha) AS varchar(2)), 2) AS Periodo,
SUM(g.Valor * (1 * g.IVAVal) AS ValorGraph,
SUM(g.Valor * (1 * g.IVAVal) OVER () AS GastosTot
FROM dbo.Gastos as g
GROUP BY datepart(year, g.Fecha), datepart(month, g.Fecha)
)
SELECT g.Periodo,
COALESCE(v.VentasRaw, 0), COALESCE(v.VentasTot, 0),
g.ValorGraph, g.GastosTot
FROM Ventas as v RIGHT OUTER JOIN Gastos g ON g.Periodo = v.Periodo;
You could certainly use a full outer join if it's possible to have sales but no expenses.

Related

How do you do a where clause with a select statement as a value

I'm trying to get the total amount from the next month's record.
SELECT TOP (1000)
[DEMAND_POINT_ID],
[DPM_XREF_ID],
[UTIL_TYPE],
[ACCOUNT_ID],
[REV_YR_MNTH],
[TOTAL_AMOUNT],
(SELECT CAST(LEFT((SELECT CONVERT(varchar, DATEADD(month, 1, LEFT(REV_YR_MNTH, 4) + '-' + RIGHT(REV_YR_MNTH, 2) + '-' + '01'), 112)), 6) AS int)) AS nextmonth,
(SELECT de2.[TOTAL_AMOUNT]
FROM [DEQEnergyUsage].[dbo].[DST_ENERGY_USAGE_AGGR] de2
WHERE de2.ACCOUNT_ID = de.ACCOUNT_ID
AND de2.REV_YR_MNTH = (SELECT CAST(LEFT((SELECT CONVERT(varchar, DATEADD(month, 1, LEFT(REV_YR_MNTH, 4) + '-' + RIGHT(REV_YR_MNTH, 2) + '-' + '01'), 112)), 6) AS int))) AS nextamount
FROM
[DEQEnergyUsage].[dbo].[DST_ENERGY_USAGE_AGGR] de
WHERE
ACCOUNT_ID =1
ORDER BY
ACCOUNT_ID, de.REV_YR_MNTH
The value nextmonth creates the next month's rev_yr_mnth correctly however when I try to use this created value in the where clause it brings back nothing in the nextamount field.
It's like it does not recognize the created value as a true value. What do I need to do to have the where clause recognize the value?
Using CTE you can add condition on nextmonth.
With CTE As (
SELECT TOP (1000)
[DEMAND_POINT_ID],
[DPM_XREF_ID],
[UTIL_TYPE],
[ACCOUNT_ID],
[REV_YR_MNTH],
[TOTAL_AMOUNT],
(SELECT CAST(LEFT((SELECT CONVERT(varchar, DATEADD(month, 1, LEFT(REV_YR_MNTH, 4) + '-' + RIGHT(REV_YR_MNTH, 2) + '-' + '01'), 112)), 6) AS int)) AS nextmonth,
(SELECT de2.[TOTAL_AMOUNT]
FROM [DEQEnergyUsage].[dbo].[DST_ENERGY_USAGE_AGGR] de2
WHERE de2.ACCOUNT_ID = de.ACCOUNT_ID
AND de2.REV_YR_MNTH = (SELECT CAST(LEFT((SELECT CONVERT(varchar, DATEADD(month, 1, LEFT(REV_YR_MNTH, 4) + '-' + RIGHT(REV_YR_MNTH, 2) + '-' + '01'), 112)), 6) AS int))) AS nextamount
FROM
[DEQEnergyUsage].[dbo].[DST_ENERGY_USAGE_AGGR] de
WHERE
ACCOUNT_ID =1
ORDER BY
ACCOUNT_ID, de.REV_YR_MNTH
)
select * from CTE
-- Where nextmonth condition

Multiply TIME in SQL Server

I have this query below, basically I'm trying to subtract 2 dates and get the hours.
However, I need the subtracted time to be multiplied by the number of cleaners
SELECT
CONVERT(TIME, ClientBooking.TimeEnd - ClientBooking.TimeStart) AS HoursWorked2,
ClientBooking.NumberOfCleaners AS NumberOfCleaners,
ClientBooking.TimeStart,
ClientBooking.TimeEnd,
ClientBooking.ClientID,
((((ClientInfo.FirstName + N' ') +
ClientInfo.LastName) + N' ') +
ClientInfo.Company) AS ClientName,
((((ClientInfo.Address + N' - ') +
ClientInfo.City) + N' - ') +
ClientInfo.ZipCode) AS Address,
((ClientInfo.PhoneNumber + N' ') +
ClientInfo.EmailAddress) AS Contact,
(ClientBooking.HourlyRate / 60) AS MinRate,
(DATEDIFF(MINUTE,ClientBooking.TimeStart,ClientBooking.TimeEnd) * ClientBooking.NumberOfCleaners) AS Quantity,
ClientBooking.HourlyRate,
DATEDIFF(HOUR, ClientBooking.TimeStart, ClientBooking.TimeEnd) AS HoursWorked
FROM
(dbo.ClientBooking ClientBooking
INNER JOIN
dbo.ClientInfo ClientInfo ON (ClientInfo.ClientID = ClientBooking.ClientID))
Basically, I need to multiply the result of this:
CONVERT(TIME,"ClientBooking"."TimeEnd" - "ClientBooking"."TimeStart" )
How About using this:
Select
convert(time,DATEADD(MINUTE, ( convert(float,(DATEDIFF(minute, ClientBooking.TimeStart, ClientBooking.TimeEnd) * ClientBooking.NumberOfCleaners))/60), ''))
FROM
(dbo.ClientBooking ClientBooking
INNER JOIN
dbo.ClientInfo ClientInfo ON (ClientInfo.ClientID = ClientBooking.ClientID))
Sorry if i have missed a parenthesis !!
You can use DATEDIFF() function..
Something like:
DATEDIFF(hour, ClientBooking.TimeStart, ClientBooking.TimeEnd) * ClientBooking.NumberOfCleaners
as your desired column!
If I understand you correctly this could help you:
declare #start datetime = '2018-11-02 07:00:00'
declare #end datetime = '2018-11-02 08:03:00'
declare #diff int
Select #diff = DATEDIFF(minute,#start,#end)
Select case
when #diff < 60 then concat('00:', right('0' + convert(varchar,#diff), 2))
when #diff >= 60 and #diff < 120 then '01:' + right('0' + convert(varchar,#diff - 60), 2)
when #diff >= 120 and #diff < 180 then '02:' + right('0' + convert(varchar,#diff - 120), 2)
when #diff >= 180 and #diff < 240 then '03:' + right('0' + convert(varchar,#diff - 180), 2)
end
Of course you would need to add the following hours as well.
I've splitted everything up, so it is easier to understand. But you should be able to write it in one line and without variables as well
Hope this helps.

Trying to round the results of a sql query to 2 decimals

In the following query, I'm trying to return the data with 2 decimal places (.00) for the SUM line:
SELECT
CONVERT(varchar, YEAR(COALESCE(release_date, requested_date)))
+ RIGHT('00' + CONVERT(varchar, MONTH(COALESCE(release_date, requested_date))), 2) AS yrmnth,
salesrep,
customer_name,
SUM(price_per_ea * COALESCE(open_release_qty, open_order_qty)) AS ext_price
you could use convert for take control over the format
SELECT
CONVERT(VARCHAR, YEAR(COALESCE(release_date, requested_date)))
+ RIGHT('00' + CONVERT(VARCHAR,
MONTH(COALESCE(release_date, requested_date))),2) as yrmnth
,salesrep
,customer_name
, Convert(decimal(12,2),
SUM(price_per_ea * COALESCE(open_release_qty, open_order_qty))) as ext_price
Use the round function in your select:
round(SUM(price_per_ea * COALESCE(open_release_qty, open_order_qty)),2)

Employee Total Experience SQL query

I have table with employees work experience. I want to get summary experience in format like yy mm dd.
e_id work_from work_to
2 2003-10-13 2004-02-12
2 2004-02-16 2004-06-30
2 2004-07-01 2006-01-31
2 2006-02-01 2017-07-12
Result should be: 13Y 8M 27D
Query like:
sum(datediff(month,work_from,work_to))/12,
sum(datediff(month,work_from,work_to)%12
works fine, but what about days?
Please note, the following query is a general summation which does not include leap years and the months are averaged between 365/12 in days since the amount of days in each month vary. If you want an exact figure that includes the exact amount of days, the algorithm will be more involved, but hopefully this gets you in a reasonably close ballpark figure.
SELECT CONVERT(VARCHAR(10), sum(datediff(year,work_from,work_to))-1) + 'Y' AS Years,
CONVERT(VARCHAR(10), FLOOR((sum(datediff(day, work_from,work_to)) - ((sum(datediff(year,work_from,work_to)) - 1) * 365)) / 30.4166)) + 'M' AS Months,
CONVERT(VARCHAR(10), CEILING(sum(datediff(day, work_from,work_to)) - ((sum(datediff(year,work_from,work_to)) - 1) * 365) - (FLOOR((sum(datediff(day, work_from,work_to)) - ((sum(datediff(year,work_from,work_to)) - 1) * 365)) / 30.4166) * 30.4166))) + 'D' AS Days,
CONVERT(VARCHAR(10), sum(datediff(day,work_from,work_to))) AS Total_Days
Here is my solution. This is the closest I can get. The problem that I had is that I cant escape the M after month.
DECLARE #SumExp Datetime = (SELECT CONCAT(
DATENAME(day, (SELECT SUM(DATEDIFF(day, WorkFrom, WorkTo))
FROM EmployeeWorkExperience)),
DATENAME(month, (SELECT SUM(DATEDIFF(day, WorkFrom, WorkTo))
FROM EmployeeWorkExperience)),
DATENAME(year, (SELECT SUM(DATEDIFF(day, WorkFrom, WorkTo))
FROM EmployeeWorkExperience))))
SELECT REPLACE(FORMAT(#SumExp, 'yyY MM# ddD'), '#', 'M')
DECLARE #work TABLE(
WorkId INT IDENTITY(1,1) PRIMARY KEY ,
work_from DATETIME NOT NULL,
work_to DATETIME NOT NULL )
INSERT INTO #work
( work_from, work_to )
VALUES ( '10/13/2003',
'2/12/2004'
),
(
'2/16/2004',
'6/30/2004'
),
('7/1/2004',
'1/31/2006'
),
('2/1/2006',
'7/12/2017'
)
DECLARE #seconds int
SELECT #seconds = SUM(DATEDIFF(SECOND, work_from, work_to))
FROM #work
DECLARE #VARDT DATETIME = DATEADD(SECOND, #seconds, 0)
SELECT CAST(DATEPART(YEAR, #VARDT) - 1900 AS VARCHAR(10)) + ' year(s) ' + CAST(DATEPART(MONTH, #VARDT) - 1 AS VARCHAR(2)) + ' month(s) '
+ CAST(DATEPART(DD, #VARDT) - 1 AS VARCHAR(2)) + ' day(s) ' + CAST(DATEPART(HOUR, #VARDT) AS VARCHAR(2)) + ' hour(s) '
+ CAST(DATEPART(MINUTE, #VARDT) AS VARCHAR(2)) + ' minute(s) ' + CAST(DATEPART(SECOND, #VARDT) AS VARCHAR(2)) + ' second(s)'

Combining two rows in one select query

Hi I am having a hard time combining two records (from a single table) on a single query. The idea is, DATE_FIELD column is a date type and ColA is an integer data type.
To further illustrate my inquiry, I have attached an image below
1.) Is the raw table.
2.) Is the desired output.
P.S. The filter for DATE_FIELD is not a simple "WHERE DATE_FIELD IN" clause.
For example, I wanted to get the DATE_FIELD=12/30/2013. Then I need to get the Previous Sept DATE_FIELD also, which is 9/30/2013 programatically by using this query that I got from the web:
CASE
WHEN MONTH(DATE_FIELD) < 10
THEN
(cast(CAST((DATE_FIELD) - 1) as char(4)) + RIGHT('00' + LTRIM(09),2) + RIGHT('00' + LTRIM(30),2) AS Date))
ELSE
( cast(CAST((YEAR(DATE_FIELD)) as char(4)) + RIGHT('00' + LTRIM(09),2) + RIGHT('00' + LTRIM(30),2) AS Date))
END
Here is my current sql script (which cannot get the ColA equivalent for Previous Sept filter:
SELECT DATE_FIELD, ColA,
CASE
WHEN MONTH(DATE_FIELD) < 10
THEN
(cast(CAST((YEAR(DATE_FIELD) - 1) as char(4)) + RIGHT('00' + LTRIM(09),2) + RIGHT('00' + LTRIM(30),2) AS Date))
ELSE
( cast(CAST((YEAR(DATE_FIELD)) as char(4)) + RIGHT('00' + LTRIM(09),2) + RIGHT('00' + LTRIM(30),2) AS Date))
END AS PREVIOUS,
(
SELECT ColA
FROM TABLE_A
WHERE DATE_FIELD =
CASE
WHEN MONTH(DATE_FIELD) < 10
THEN
(cast(CAST((YEAR(DATE_FIELD) - 1) as char(4)) + RIGHT('00' + LTRIM(09),2) + RIGHT('00' + LTRIM(30),2) AS Date))
ELSE
( cast(CAST((YEAR(DATE_FIELD)) as char(4)) + RIGHT('00' + LTRIM(09),2) + RIGHT('00' + LTRIM(30),2) AS Date))
END
) AS PYE_colA
FROM TABLE_A
WHERE DATE_FIELD = '12/30/2013'
Thanks!
Do a cross join with the same table and use your CASE structure only in the where clause:
SELECT a.DATE_FIELD AS DATE_FIELD_1,
a.ColA AS ColA_1,
b.DATE_FIELD AS DATE_FIELD_2,
b.ColA AS ColA_2
FROM TABLE_A a
CROSS JOIN TABLE_A b
WHERE DATE_FIELD_1 = 'your date'
AND DATE_FIELD_2 = (
CASE
WHEN MONTH(DATE_FIELD_1) < 10
THEN
(cast(CAST((YEAR(DATE_FIELD_1) - 1) as char(4)) + RIGHT('00' + LTRIM(09),2) + RIGHT('00' + LTRIM(30),2) AS Date))
ELSE
( cast(CAST((YEAR(DATE_FIELD_1)) as char(4)) + RIGHT('00' + LTRIM(09),2) + RIGHT('00' + LTRIM(30),2) AS Date))
END)
;
Another possibility based on Thorsten Kettners remarks:
SELECT a.DATE_FIELD AS DATE_FIELD_1,
a.ColA AS ColA_1,
b.DATE_FIELD AS DATE_FIELD_2,
b.ColA AS ColA_2
FROM TABLE_A a
INNER JOIN TABLE_A b
ON b.DATE_FIELD = (
CASE
WHEN MONTH(a.DATE_FIELD) < 10
THEN
(cast(CAST((YEAR(a.DATE_FIELD) - 1) as char(4)) + RIGHT('00' + LTRIM(09),2) + RIGHT('00' + LTRIM(30),2) AS Date))
ELSE
( cast(CAST((YEAR(a.DATE_FIELD)) as char(4)) + RIGHT('00' + LTRIM(09),2) + RIGHT('00' + LTRIM(30),2) AS Date))
END)
WHERE a.DATE_FIELD = 'your date'
;