Sql select sub query with count - sql

Ok so i have 3 tables :
[AXprod].[dbo].[RMSPOSINVOICE],[AXPROD].[dbo].[discountcard] ,[IntegrationProd].[dbo].[POS_KvitoGalva]. And i want to find out when discount card was used more than once in one inventlocation and time when it was used. The table [IntegrationProd].[dbo].[POS_KvitoGalva] has these times. I use this code to get the time each card was used each day is:
sELECT a.discountcardid,count(a.discountcardid)
FROM [AXprod].[dbo].[RMSPOSINVOICE] a
inner join [AXPROD].[dbo].[discountcard] b
on a.discountcardid = b.discountcardid
inner join [IntegrationProd].[dbo].[POS_KvitoGalva] c
on a.possalesid = c.id
where a.dataareaid = 'ermi' and len(a.discountcardid) > '0' and b.dataareaid = 'ermi' and ('500' = a.inventlocationid )
and (a.invoicedate >= '2015-04-22 00:00:00.000' and a.invoicedate <= '2015-04-22 00:00:00.000')
group by a.discountcardid,a.inventlocationid,a.posnumber
having count(a.discountcardid) > '1'
And i get the following result:
DISCOUNTCARDID COUNT
123456 2
145962 2
and i have a query to find when each card was used (date and time)
SELECT a.discountcardid,a.inventlocationid,a.posnumber,year,month,day,hour,minute,c.id
FROM [AXprod].[dbo].[RMSPOSINVOICE] a
inner join [AXPROD].[dbo].[discountcard] b
on a.discountcardid = b.discountcardid
inner join [IntegrationProd].[dbo].[POS_KvitoGalva] c
on a.possalesid = c.id
where a.dataareaid = 'ermi' and len(a.discountcardid) > '0' and b.dataareaid = 'ermi' and ('500' = a.inventlocationid )
and (a.invoicedate >= '2015-04-22 00:00:00.000' and a.invoicedate <= '2015-04-22 00:00:00.000')
group by a.discountcardid,a.inventlocationid,a.posnumber,year,month,day,hour,minute,c.id
order by DISCOUNTCARDID
And i get the result:
discountcardid inventlocationid posnumber year month day hour minute id
123456 500 7 2015 4 22 12 44 6355302
123456 500 7 2015 4 22 14 24 6355302
145962 500 7 2015 4 22 13 56 6355302
145962 500 7 2015 4 22 13 24 6355302
145555 500 7 2015 4 22 12 11 5465465
The problem:
I dont want to get discount cards that were only used once so i try this:
SELECT a.discountcardid,a.inventlocationid,a.posnumber,year,month,day,hour,minute,c.id,
( sELECT count(s.discountcardid)
FROM [AXprod].[dbo].[RMSPOSINVOICE] s
inner join [AXPROD].[dbo].[discountcard] b
on s.discountcardid = b.discountcardid
inner join [IntegrationProd].[dbo].[POS_KvitoGalva] c
on s.possalesid = c.id
where s.dataareaid = 'ermi' and len(s.discountcardid) > '0' and b.dataareaid = 'ermi' and ('500' = s.inventlocationid )
and (s.invoicedate >= '2015-04-22 00:00:00.000' and s.invoicedate <= '2015-04-22 00:00:00.000') and s.DISCOUNTCARDID = a.DISCOUNTCARDID
group by s.discountcardid,s.inventlocationid,s.posnumber
having count(a.discountcardid) > '1')
FROM [AXprod].[dbo].[RMSPOSINVOICE] a
inner join [AXPROD].[dbo].[discountcard] b
on a.discountcardid = b.discountcardid
inner join [IntegrationProd].[dbo].[POS_KvitoGalva] c
on a.possalesid = c.id
where a.dataareaid = 'ermi' and len(a.discountcardid) > '0' and b.dataareaid = 'ermi' and ('500' = a.inventlocationid )
and (a.invoicedate >= '2015-04-22 00:00:00.000' and a.invoicedate <= '2015-04-22 00:00:00.000')
group by a.discountcardid,a.inventlocationid,a.posnumber,year,month,day,hour,minute,c.id
order by DISCOUNTCARDID
But all i get is the same number of values and NULL in the last field in all columns. I hope i made myself clear ;).

You should be able to call the query once and use an windowed function in order to get the count. I don't believe you can use an analytic function in the where statement so I added an additional SELECT statement in order to add the WHERE > 1 for the count.
SELECT *
FROM (SELECT
a.discountcardid,
a.inventlocationid,
a.posnumber,
year,
month,
day,
hour,
minute,
c.id,
COUNT(*) OVER (PARTITION BY a.discountcardid, a.inventlocationid, a.posnumber) AS CardCount
FROM AXprod.dbo.RMSPOSINVOICE a
JOIN AXprod.dbo.discountcard b
ON b.discountcardid = a.discountcardid
JOIN IntegrationProd.dbo.POS_KvitoGalva c
ON c.id = a.possalesid
WHERE a.dataareaid = 'ermi'
AND len(a.discountcardid) > '0'
AND b.dataareaid = 'ermi'
AND a.inventlocationid = 500
AND a.invoicedate >= '2015-04-22 00:00:00.000'
AND a.invoicedate <= '2015-04-22 00:00:00.000'
) d
WHERE d.CardCount > 1
ORDER BY d.discountcardid

Related

Oracle query running indefinitely

I have a query that is somewhat running indefinitely, I might be going in circles here but it doesn't give me an error, just continuously runs.
Query
WITH calendar AS (
SELECT dt
FROM (
SELECT TRUNC(LAST_DAY(TO_DATE(&month || '/01/' || &year, 'MM/DD/YYYY')) - ROWNUM + 1) AS dt
FROM DUAL CONNECT BY ROWNUM <= 31
)
WHERE dt >= TRUNC(TO_DATE(&month || '/01/' || &year, 'MM/DD/YYYY'), 'MM')
ORDER BY dt ASC
)
SELECT
cal.dt,
(
SELECT NVL(SUM(rd.rate_value), 0.00)
FROM cage_dates cd
LEFT JOIN cages c ON c.id = cd.cage_id
LEFT JOIN rent_item ri ON ri.id = c.rent_item_id
LEFT JOIN rate_dates rd ON rd.rent_item_id = c.rent_item_id
WHERE c.group_id = 123
AND c.room_id = 456
AND (
LOWER(c.cage_use) NOT IN ('created in error', 'prod setup')
OR c.cage_use IS NULL
)
AND ri.display_name NOT LIKE '% BR'
AND TRUNC(cd.added) <= cal.dt
AND (
CASE
WHEN cd.removed IS NOT NULL AND TRUNC(cd.removed) >= cal.dt THEN 1
WHEN cd.removed IS NULL THEN 1
ELSE 0
END = 1
)
AND TRUNC(rd.effective_on) <= cal.dt
AND (
CASE
WHEN rd.effective_until IS NOT NULL AND TRUNC(rd.effective_until) >= cal.dt THEN 1
WHEN rd.effective_until IS NULL THEN 1
ELSE 0
END = 1
)
) AS per_diem
FROM calendar cal
ORDER BY cal.dt ASC
When I change cal.dt to TO_DATE('10/25/2017', 'MM/DD/YYYY') it gives me the correct SUM of the all the rates for that day but attaches it to all the days like so:
dt per_diem
--------------------
10/01/2017 1.19
10/02/2017 1.19
...
10/31/2017 1.19
Whereas I want something like:
dt per_diem
--------------------
10/01/2017 0
10/02/2017 1.19
10/03/2017 2.52
...
10/31/2017 0.67
Anyone have any idea?
I would move the subquery into a join, like this:
WITH calendar AS (
SELECT dt
FROM (
SELECT TRUNC(LAST_DAY(TO_DATE(&month || '/01/' || &year, 'MM/DD/YYYY')) - ROWNUM + 1) AS dt
FROM DUAL CONNECT BY ROWNUM <= 31
)
WHERE dt >= TRUNC(TO_DATE(&month || '/01/' || &year, 'MM/DD/YYYY'), 'MM')
)
SELECT cal.dt, NVL(SUM(rd.rate_value), 0.00)
FROM Calendar cal
LEFT JOIN cage_dates cd ON TRUNC(cd.added) = cal.dt
LEFT JOIN cages c ON c.id = cd.cage_id
LEFT JOIN rent_item ri ON ri.id = c.rent_item_id
LEFT JOIN rate_dates rd ON rd.rent_item_id = c.rent_item_id
WHERE c.group_id = 123
AND c.room_id = 456
AND (
LOWER(c.cage_use) NOT IN ('created in error', 'prod setup')
OR c.cage_use IS NULL
)
AND ri.display_name NOT LIKE '% BR'
AND (
CASE
WHEN cd.removed IS NOT NULL AND TRUNC(cd.removed) >= cal.dt THEN 1
WHEN cd.removed IS NULL THEN 1
ELSE 0
END = 1
)
AND TRUNC(rd.effective_on) <= cal.dt
AND (
CASE
WHEN rd.effective_until IS NOT NULL AND TRUNC(rd.effective_until) >= cal.dt THEN 1
WHEN rd.effective_until IS NULL THEN 1
ELSE 0
END = 1
GROUP BY cal.dt
But I am not sure what you are actually attempting with the TRUNC(cd.added) <= cal.dt at first thought is was a running sum, but perhaps you are trying to make a value appear on every day?

Displaying data twice in SQL Server

I have the following table:
Date Type Amount
Jul-17 Type A 20
Jul-17 Type B 30
Jul-17 Type C 10
Aug-17 Type A 50
Aug-17 Type D 40
Aug-17 Type C 70
My query will only filters two month as below:
SELECT DATE, Type, Amount FROM Table 1 WHERE DATE >= '01-Jul-2017'
AND DATE <= '31-Aug-2017'
I want to display Type that does not exist in July and display amount 0 and Type that does not exist in August and display Amount 0 as below:
Date Type Amount
Jul-17 Type A 20
Aug-17 Type A 50
Jul-17 Type B 30
Aug-17 Type B 0
Jul-17 Type C 10
Aug-17 Type C 70
Jul-17 Type D 0
Aug-17 Type D 40
So far I have tried below, but it's affecting performance. I want to simplify the query without using union:
SELECT DATE, Type, Amount
FROM Table 1
WHERE DATE >= '01-Jul-2017'
AND DATE <= '31-Aug-2017'
Union
SELECT '01-Jul-2017' AS DATE, TYPE, 0 AS AMOUNT
WHERE DATE >= '01-Aug-2017'
AND DATE <= '31-Aug-2017'
AND Type NOT in (SELECT DISTINCT TYPE WHERE DATE >= '01-Jul-2017'
AND DATE <= '31-Jul-2017')
Union
SELECT '01-Aug-2017' AS DATE, TYPE, 0 AS AMOUNT
WHERE DATE >= '01-Jul-2017'
AND DATE <= '31-Jul-2017'
AND Type NOT in (SELECT DISTINCT TYPE WHERE DATE >= '01-Aug-2017'
AND DATE <= '31-Aug-2017')
You can use a cross join to get all possible combinations and then use a left outer join to get the actual amount...
WITH cte
AS ( SELECT DISTINCT
t1.Date ,
t2.Type
FROM dbo.Table1 t1
CROSS JOIN dbo.Table1 t2
WHERE t1.Date BETWEEN '2017-07-01' AND '2017-08-31'
AND t2.Date BETWEEN '2017-07-01' AND '2017-08-31'
)
SELECT cte.Date ,
cte.Type ,
COALESCE(t.Amount, 0) AS Amount
FROM cte
LEFT OUTER JOIN dbo.Table1 AS t ON t.Date = cte.Date
AND t.Type = cte.Type;
At begininng I misred your tags, and thought you should do for MYSQL (apologize). I leave MYSQL version at end.
I used * for brevity. Pls change it with full name list for your final query.
THIS IS MSSQL VERSION
You need to specify two dates just one time (at the beginning).
I used TN as table name.
WITH TT AS (SELECT *, MONTH(DATE) AS MN from TN WHERE DATE >= '2017-07-01' AND DATE <= '2017-08-31' )
SELECT COALESCE(C.DATE, CASE WHEN C.MN2=MINMN THEN DATEADD(MONTH,+1, C.DATE2) ELSE DATEADD(MONTH,-1, C.DATE2) END) AS DATE
, COALESCE(C.TYPE, C.TYPE2) AS TYPE
, COALESCE(C.AMOUNT,0) AS AMOUNT
FROM
(SELECT A.*, B.DATE AS DATE2, B.TYPE AS TYPE2, B.AMOUNT AS AMOUNT2, B.MN AS MN2, X.MINMN
from TT A
FULL JOIN TT B ON A.MN<>B.MN AND A.TYPE = B.TYPE
CROSS JOIN (SELECT MIN(MN) MINMN FROM TT) X
)C
ORDER BY DATE, TYPE ;
Output:
DATE TYPE AMOUNT
1 17.07.2017 00:00:00 A 20
2 17.07.2017 00:00:00 B 30
3 17.07.2017 00:00:00 C 10
4 17.07.2017 00:00:00 D 0
5 17.08.2017 00:00:00 A 50
6 17.08.2017 00:00:00 B 0
7 17.08.2017 00:00:00 C 70
8 17.08.2017 00:00:00 D 40
MYSQL VERSION
At moment I just find this one (it use only one UNION, instead of two).
I used TN as table name. Pls check it for performances.
SELECT * from TN WHERE DATE >= '2017-07-01' AND DATE <= '2017-08-31'
UNION ALL
SELECT CASE WHEN MONTH(A.DATE)=M1 THEN '2017-08-01' ELSE '2017-07-01' END AS DATE, A.TYPE, 0 AS AMOUNT
from TN A
LEFT JOIN (SELECT * FROM TN WHERE DATE >= '2017-07-01' AND DATE <= '2017-08-31') B ON DATE_FORMAT(A.DATE ,'%Y-%m-01') <> DATE_FORMAT(B.DATE ,'%Y-%m-01') AND A.TYPE = B.TYPE
CROSS JOIN (SELECT MIN(MONTH(DATE)) AS M1 FROM TN WHERE DATE >= '2017-07-01' AND DATE <= '2017-08-31') X
WHERE A.DATE >= '2017-07-01' AND A.DATE <= '2017-08-31'
AND B.TYPE IS NULL
;
Output:
Date type amount
1 2017-07-17 A 20
2 2017-07-17 B 30
3 2017-07-17 C 10
4 2017-08-17 A 50
5 2017-08-17 D 40
6 2017-08-17 C 70
7 2017-08-01 B 0
8 2017-07-01 D 0
If you wanna execute the result using pivot.
You may try the below query:
select [Date] As Date_For_Table,
[Type A] =
case
when [Type A] IS NULL Then 0
else [Type A]
end,
[Type B] =
case
when [Type B] IS NULL Then 0
else [Type B]
end,
[Type C] =
case
when [Type C] IS NULL Then 0
else [Type C]
end,
[Type D] =
case
when [Type D] IS NULL Then 0
else [Type D]
end
from
(
select CONVERT(CHAR(4), date, 100) + CONVERT(CHAR(4), date, 120) as date ,type,Amount
from Table1
) as PivotData
pivot
(
avg(amount) for type in
([Type A],[Type B],[Type C],[Type D]))as Pivoting
order by date desc
output:

Get multiple rows from query

SELECT USERINFO.UserID
,SUM(DATEDIFF(DAY, DateFrom, DateTo) + 1) AS total_leave_days
FROM USERINFO
INNER JOIN CHECKINOUT ON USERINFO.USERID = CHECKINOUT.USERID
LEFT OUTER JOIN DEPARTMENTS ON DEPARTMENTS.DEPTID = USERINFO.DEFAULTDEPTID
left join AuthLeave on AuthLeave.userid = userinfo.userid
and AuthLeave.DATEFROM>='2014-01-01'
and AuthLeave.DATETO<='2014-06-30'
WHERE (CHECKINOUT.CHECKTIME >= '2014-01-01')
AND (CHECKINOUT.CHECKTIME <= '2014-06-30')
AND DEPARTMENTS.DEPTNAME = 'GEN/SUP-TBL'
GROUP BY USERINFO.UserID
here is my code from this i can get below out put
UserID total_leave_days
35 NULL
350 NULL
30 NULL
10 735
167 NULL
21 920
1 621
224 NULL
so it is not correct my Authleave table data is below:
UserID DATEFROM DATETO
1 2014-03-10 2014-03-15
10 2014-05-28 2014-05-29
21 2014-05-27 2014-05-27
1 2014-04-10 2014-04-15
from now i want output like below:
UserID total_leave_days
35 NULL
350 NULL
30 NULL
10 2
167 NULL
21 1
1 12
224 NULL
so how can i do this ?
Can you try This SQL
SELECT U.[UserID],
C.[Leave]
FROM [USERINFO] U
LEFT JOIN (SELECT [UserID],
SUM(DATEDIFF(DAY,[DATEFROM],[DATETO])) [Leave]
FROM [CHECKINOUT]
WHERE [CHECKTIME] >= '2014-01-01' AND [CHECKTIME] <= '2014-06-30'
GROUP BY [UserID]
) C ON U.[USERID] = C.[USERID]
LEFT JOIN [DEPARTMENTS] D ON D.[DEPTID] = U.[DEFAULTDEPTID]
LEFT JOIN [AuthLeave] A on A.[userid] = U.[userid]
WHERE D.[DEPTNAME] = 'GEN/SUP-TBL'
enter code here
Edit:
See the below SQL, I got a bit confused by Table Names.
SELECT U.[UserID],
L.[Leave]
FROM [USERINFO] U
JOIN CHECKINOUT C ON U.USERID = C.USERID
LEFT JOIN [DEPARTMENTS] D ON D.[DEPTID] = U.[DEFAULTDEPTID]
LEFT JOIN (SELECT [UserID],
SUM(DATEDIFF(DAY,[DATEFROM],[DATETO])) [Leave]
FROM [AuthLeave]
WHERE [DATEFROM] >= '2014-01-01' AND [DATETO] <= '2014-06-30'
GROUP BY [UserID]
) L ON L.[UserID] =U.[UserID]
WHERE D.[DEPTNAME] = 'GEN/SUP-TBL'
AND C.[CHECKTIME] >= '2014-01-01' AND C.[CHECKTIME] <= '2014-06-30'

Adding Year and Month to a group by sql query

I have the following query where #BeginTime is the first day of the month and #EndTime is the last day of the month .
SET #BeginTime = RTRIM(#BeginTime) + ' 00:00:00'
SET #EndTime = RTRIM(#EndTime) + ' 23:59:59'
select C.Name , COUNT(DISTINCT D.Id) from DriverLic D
INNER JOIN Clov C WITH (NOLOCK) ON D.CId = C.CId
AND ((D.RDate < #EndTime)
AND ( D.UrDate > #BeginTime))
group by C.Name
I get an output something like this :
Name Count(D.Id)
AC 22
AB 32
CD 11
I would like to get an output something like this :
Year Month Name Count(D.id)
2013 8 AC 22
2013 8 AB 32
2013 8 CD 11
Is there a way i can achieve this ?
Yep,
SELECT Year(yourDateColumn) AS 'Year', Month(yourDateColumn) AS 'Month', C.Name, COUNT(DISTINCT D.Id)
from DriverLic D
INNER JOIN Clov C WITH (NOLOCK) ON D.CId = C.CId
--WHERE your where conditions should go here...
GROUP BY
Year(yourDateColumn), Month(yourDateColumn), C.Name

Calculating Over Top N Values Per Group

I have an access table with time series data like this:
loc | date | value
A 2/11/07 50
A 2/12/07 45
A 2/13/07 23
B 2/11/07 34
B 2/12/07 46
B 2/13/07 56
C ....... ...
...
D..........
.....
And I want to get the Z, (value - avg(values)/stDev(values), values of each group over different time periods so the 20 z values would consider values over the last 20 days, the 60 day over the last 60 days etc. And I also want to select the z values on the latest day so the result would look like this:
loc | date | value | 20Day zValue | 60Day ZValue | 120 day Zvalue
A 2/13/07 23 .04 .09 .6
B 2/13/07 56 .87 .54 .96
C .....................
Try this:
SELECT
a.*,
b.20Day_zValue,
c.60Day_zValue,
d.120Day_zValue
FROM
(
SELECT aa.loc, aa.date, aa.value
FROM tbl aa
INNER JOIN
(
SELECT loc, MAX(date) AS maxdate
FROM tbl
GROUP BY loc
) bb ON aa.loc = bb.loc AND aa.date = bb.maxdate
) a
INNER JOIN
(
SELECT loc, AVG(value)/StDev(value) AS 20Day_zValue
FROM tbl
WHERE date >= DateAdd('d', -20, Date())
GROUP BY loc
) b ON a.loc = b.loc
INNER JOIN
(
SELECT loc, AVG(value)/StDev(value) AS 60Day_zValue
FROM tbl
WHERE date >= DateAdd('d', -60, Date())
GROUP BY loc
) c ON a.loc = c.loc
INNER JOIN
(
SELECT loc, AVG(value)/StDev(value) AS 120Day_zValue
FROM tbl
WHERE date >= DateAdd('d', -120, Date())
GROUP BY loc
) d ON a.loc = d.loc
I came up with a solution, zane bien answer was a good start. the main problem was ms access's requirement for multiple joins. the basic bracketing structure is like this:
Select a,b,c
FROM
(((Table1 Inner Join Table2 ON a = b)
Inner Join Table3 ON a = c)
Inner Join Table4 ON a = d)
And the solution to my problem is:
SELECT A.loc AS Location, A.value AS Value,
(A.value - B.OneMonthAvg) / B.OneMonthStdev AS OneMonthZscore,
(A.value - C.ThreeMonthAvg) / C.ThreeMonthStdev AS ThreeMonthZscore,
(A.value - D.SixMonthAvg) / D.SixMonthStdev AS SixMonthZscore,
(A.value - E.OneYearAvg) / E.OneYearStdev AS OneYearZscore,
(A.value - F.TwoYearAvg) / F.TwoYearStdev AS TwoYearZscore,
(A.value - G.ThreeYearAvg) / G.ThreeYearStdev AS ThreeYearZscore
FROM
(((((((tbl AS A
INNER JOIN
(SELECT loc, AVG(value) AS OneMonthAvg, STDEV(value) AS OneMonthStdev
FROM tbl
WHERE date >= DateAdd('m', -1, Date())
GROUP BY loc)
AS B ON A.loc = B.loc)
INNER JOIN
(SELECT loc, AVG(value) AS ThreeMonthAvg, STDEV(value) AS ThreeMonthStdev
FROM tbl
WHERE date >= DateAdd('m', -3, Date())
GROUP BY loc)
AS C ON A.loc = C.loc)
INNER JOIN
(SELECT loc, AVG(value) AS SixMonthAvg, STDEV(value) AS SixMonthStdev
FROM tbl
WHERE date >= DateAdd('m', -6, Date())
GROUP BY loc)
AS D ON A.loc = D.loc)
INNER JOIN
(SELECT loc, AVG(value) AS OneYearAvg, STDEV(value) AS OneYearStdev
FROM tbl
WHERE date >= DateAdd('yyyy', -1, Date())
GROUP BY loc)
AS E ON A.loc = E.loc)
INNER JOIN
(SELECT loc, AVG(value) AS TwoYearAvg, STDEV(value) AS TwoYearStdev
FROM tbl
WHERE date >= DateAdd('yyyy', -2, Date())
GROUP BY loc)
AS F ON A.loc = F.loc)
INNER JOIN
(SELECT loc, AVG(value) AS ThreeYearAvg, STDEV(value) AS ThreeYearStdev
FROM tbl
WHERE date >= DateAdd('yyyy', -3, Date())
GROUP BY loc)
AS G ON A.loc = G.loc)
Where A.date = Date()
I changed the date range which i wanted to get the z-scores for.