Assistance with creating SQL query - sql

I'm trying to create a query that only returns rows with objects that have three or more orders in a week's timeframe and are only orders submitted after 9/1/13.
SELECT OrderID, DateSubmitted, ObjectID = i.ObjectID
FROM dbo.Object i
JOIN dbo.Order j
ON i.ObjectID = j.ObjectID
WHERE DateSubmitted >= '9/1/2013'
I just can't figure out how to narrow the results to those objects with three or more orders in a week. I've tried numerous GROUP BY and HAVING clauses with no luck. Any help would be greatly appreciated.

Try:
SELECT ObjectID
FROM dbo.Object i
JOIN dbo.Order j ON J.ObjectID = i.ObjectID
WHERE DateSubmitted >= '9/1/2013'
GROUP BY ObjectID
HAVING COUNT(1) >=3

not sure but i need more info on the tables
best guess is in sql
SELECT count(OrderID), i.ObjectID
FROM dbo.Object i
JOIN dbo.Order j
ON i.ObjectID = j.ObjectID
group by i.ObjectID
having DateSubmitted >= '9/1/2013' and count(OrderID)>2

based on your last comments the query you looking for is very simple. use the DatePart function and find out WEEK of that object's order-date. check the below query. also unless you data base is configured already by default Sunday(int 7) is the First Day set in the SQL server. so you for this query purpose you need to set Monday(int 1) as the first day of Week.
you can check the current setting by executing SELECT ##DATEFIRST
if same object has places 3 or more times for multiple weeks below query is returning that object for each of that week. if you needed only Objects then you can skip weekNumber from result set and do Distinct ObjectID.
Do not forget to Reset the DATEFIRST setting to its original Value at the END.
DECLARE #Object TABLE
(
objectID INT
)
DECLARE #Order TABLE
(
orderID INT
,objectID INT
,DateSubmitted DATE
)
INSERT INTO #Object( objectID )
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
INSERT INTO #Order ( orderID, objectID, DateSubmitted )
SELECT 1,1,'10/2/2013'
UNION ALL SELECT 2,1,'10/3/2013'
UNION ALL SELECT 3,1,'10/5/2013'
UNION ALL SELECT 4,1,'10/09/2013'
UNION ALL SELECT 5,1,'10/10/2013'
UNION ALL SELECT 6,1,'10/13/2013'
UNION ALL SELECT 4,2,'10/15/2013'
UNION ALL SELECT 5,2,'10/16/2013'
UNION ALL SELECT 6,2,'10/21/2013'
UNION ALL SELECT 7,3,'09/02/2013'
UNION ALL SELECT 8,3,'09/03/2013'
UNION ALL SELECT 9,3,'09/04/2013'
DECLARE #CurrentDateFirst INT=##DATEFIRST
SET DATEFIRST 1;
SELECT i.objectID,DATEPART(week,DateSubmitted) AS weekNumber
FROM #Object i
JOIN #Order j
ON i.ObjectID = j.ObjectID
WHERE DateSubmitted >= '9/1/2013'
GROUP BY i.objectID,DATEPART(week,DateSubmitted)
HAVING(COUNT(DISTINCT orderID) >= 3)
ORDER BY i.objectID
SET DATEFIRST #CurrentDateFirst

Convoluted, but I think this will get you what you want...
With WeekSets As
(
Select i.ObjectID,
j.DateSubmitted,
j2.OrderID
From dbo.Object i
JOIN dbo.Order j
ON i.ObjectID = j.ObjectID
JOIN dbo.Order j2
On j.DateSubmitted <= j2.DateSubmitted
And j2.DateSubmitted < DateAdd(Day,7,j.DateSubmitted)
And j.ObjectID = j2.ObjectID
WHERE j.DateSubmitted >= '9/1/2013'
), GroupsOfThree As
(
Select ObjectID,
DateSubmitted
From WeekSets
Group By ObjectID,
DateSubmitted
Having Count(OrderID) >= 3
)
Select Distinct j.OrderID,
j.DateSubmitted,
w.ObjectID
From GroupsOfThree g
Join WeekSets w
On g.ObjectID = w.ObjectID
And g.DateSubmitted = w.DateSubmitted
Join dbo.Order j
On w.ObjectID = j.ObjectID
And w.OrderID = j.OrderID

Related

Variables Declaration, CTEs, and While Loops in Oracle SQL

So I might be stuck at something very trivial but can't figure out how to make it work. I create a 2 blocks of code that work in SQL but I have some problems with the date variable declaration in Oracle SQL.
I had write access to the SQL database when I create these codes so I did a 'Insert Into' to create temp tables. I don't have write access anymore. So I am using CTEs for it.
The original code looks like this:
DECLARE #Startdate Datetime = '2021-Jun-01 00:00:00.000'
DECLARE #Enddate Datetime = '2021-Jun-30 00:00:00.000'
Insert into Temp1
select ...
from ...
WHILE Startdate <= Enddate
BEGIN
Insert into Temp2
select ...
from (Temp 1)
left join
select ...
set #startdate=dateadd(d,1,#startdate)
end;
With my new code, I have made the following adjustmnets:
VARIABLE Startdate Datetime = '2021-Jun-01 00:00:00.000'
VARIABLE Enddate Datetime = '2021-Jun-30 00:00:00.000'
EXEC :Startdate := '2021-Jun-30 00:00:00.000'
EXEC :Enddate := '2021-Jun-30 00:00:00.000'
WITH Temp1 as (
select ...
from ...),
/* Unsure about using WHILE with with 2 CTEs so removing them for now but will need to be added*/
WITH Temp2 as
select ...
from (Temp 1)
left join
select ...
set startdate = :startdate + 1
end)
select * from Temp2;
The 2 blocks of code work perfectly individually. I think my concern lies with one or all of the following:
Variable Declaration - I read a couple of stackoverflow posts and it seems like there is binding variable and substitution variable. Is there a different way to declare variables?
The WHILE Loop specially between 2 CTEs. Can we do a while loop as a CTE? (similar to this) create while loop with cte
How the date is incremented. Is this the proper way to increment dates in Oracle PL/SQL?
Any guidance would be helpful.
Also adding 2 blocks of codes for reference:
Details of Tables:
Transactions - Contains Transaction information. Execution Date is a timestamp of the transaction execution
Account - Contains Account Information with a unique Account_Key for every account
Code_Rel - Maps the transaction code to a transaction type
Group Rel - Maps the transaction type to a transaction group
/***Block 1 of Code***/
insert into Temp1
select
a.ACCOUNT_KEY
,a.SPG_CD
,t.EXECUTION_DATE
from Schema_Name.TRANSACTIONS t
inner join Schema_Name.ACCOUNT a on a.en_sk=t.ac_sk
inner join Schema_Name.Code_Rel tr on t.t_cd_s = tr.t_cd_s
inner join ( select * from Schema_Name.Group_Rel
where gtrt_cd in ('Type1','Type2')) tt on tr.trt_cd = tt.trt_cd
where t.EXECUTION_DATE >= #startdate and t.EXECUTION_DATE<=#EndDt
and tt.gtrt_cd in ('Type1','Type2')
group by a.ACCOUNT_KEY ,a.SPG_CD, t.EXECUTION_DATE;
/***WHILE LOOP***/
while #startdate <= #EndDt
BEGIN
/***INSERT AND BLOCK 2 OF CODE***/
insert into Temp2
select table1.account_key, table1.SPG_CD, #startdate, coalesce(table2.sum_tr1,0),coalesce(table3.sum_tr2,0),
case when coalesce(table3.sum_tr2,0)>0 THEN coalesce(table2.sum_tr1,0)/coalesce(table3.sum_tr2,0) ELSE 0 END,
case when coalesce(table3.sum_tr2,0)>0 THEN
CASE WHEN coalesce(table2.sum_tr1,0)/coalesce(table3.sum_tr2,0)>=0.9 and coalesce(table2.sum_tr1,0)/coalesce(table3.sum_tr2,0)<=1.10 and coalesce(table2.sum_tr1,0)>=1000 THEN 'Yes' else 'No' END
ELSE 'No' END
FROM ( SELECT * FROM Temp1 WHERE execution_date=#startdate) TABLE1 LEFT JOIN
(
select a.account_key,a.SPG_CD, SUM(t.AC_Amt) as sum_tr1
from Schema_Name.TRANSACTIONS t
inner join Schema_Name.ACCOUNT a on a.en_sk=t.ac_sk
inner join Schema_Name.Code_Rel tr on t.t_cd_s = tr.t_cd_s
inner join ( select * from Schema_Name.Group_Rel
where gtrt_cd in ('Type1')) tt on tr.trt_cd = tt.trt_cd
where t.EXECUTION_DATE <= #startdate
and t.EXECUTION_DATE >=dateadd(day,-6,#startdate)
and tt.gtrt_cd in ('Type1')
group by a.account_key, a.SPG_CD
) table2 ON table1.account_key=table2.account_key
LEFT JOIN
(
select a.account_key,a.SPG_CD, SUM(t.AC_Amt) as sum_tr2
from Schema_Name.TRANSACTIONS t
inner join Schema_Name.ACCOUNT a on a.en_sk=t.ac_sk
inner join Schema_Name.Code_Rel tr on t.t_cd_s = tr.t_cd_s
inner join ( select * from Schema_Name.Group_Rel
where gtrt_cd in ('Type2')) tt on tr.trt_cd = tt.trt_cd
where t.EXECUTION_DATE <= #startdate
and t.EXECUTION_DATE >=dateadd(day,-6,#startdate)
and tt.gtrt_cd in ('Type2')
group by a.account_key, a.SPG_CD ) table3 on table1.account_key=table3.account_key
where coalesce(table2.sum_tr1,0)>=1000
set #startdate=dateadd(d,1,#startdate)
end;
You do not need to use PL/SQL or a WHILE loop or to declare variables and can probably do it all in a single SQL query using subquery factoring clauses (and recursion) to generate a calendar of incrementing dates. Something like this made-up example:
INSERT INTO temp2 (col1, col2, col3)
WITH time_bounds(start_date, end_date) AS (
-- You can declare the bounds in the query.
SELECT DATE '2021-06-01',
DATE '2021-06-30'
FROM DUAL
),
calendar (dt, end_date) AS (
-- Recursive query to generate a row for each day.
SELECT start_date, end_date FROM time_bounds
UNION ALL
SELECT dt + INTERVAL '1' DAY, end_date
FROM calendar
WHERE dt + INTERVAL '1' DAY <= end_date
),
temp1 (a, b, c) AS (
-- Made-up query
SELECT a, b, c FROM some_table
),
temp2 (a, d, e) AS (
-- Another made-up query.
SELECT t1.a,
s2.d,
s2.e
FROM temp1 t1
LEFT OUTER JOIN some_other_table s2
ON (t1.b = s2.b)
)
-- Get the values to insert.
SELECT t2.a,
t2.d,
t2.e
FROM temp2 t2
INNER JOIN calendar c
ON (t2.e = c.dt)
WHERE a BETWEEN 3.14159 AND 42;
If you try doing it with multiple inserts in a PL/SQL loop then it will be much slower than a single statement.

query on sql server

I have the following query
SELECT
A.IdDepartment,
A.IdParent,
A.Localidad,
A.Codigo,
A.Nombre,
A.Departamento,
A.Fecha,
A.[Registro Entrada],
A.[Registro Salida],
CASE
WHEN (SELECT IdUser FROM Exception WHERE IdUser = A.Codigo) <> ''
THEN(SELECT Description FROM Exception WHERE IdUser = A.Codigo AND A.Fecha BETWEEN BeginingDate AND EndingDate)
ELSE ('Ausente')
END AS Novedades
FROM VW_HORARIOS A
WHERE A.[Registro Entrada] = A.[Registro Salida]
GROUP BY A.IdDepartment,A.IdParent, A.Localidad, A.Codigo, A.Nombre, A.Departamento, A.Fecha, A.[Registro Entrada],A.[Registro Salida]
ORDER BY A.Fecha
the query performs the following selects all the records placed in the following query, what I want to validate is the following if on a date there was no record I want to create it but I do not know how to create that record because it does not exist, if someone can help me I would appreciate the help
You can try something like this. Just fill out your own Date table with values that is within your range of dates.
Remember to verify the last join. I dont know if that is the unique businesskey within your data sample
SQL Test Code
declare #DateTable table (Dates date)
insert into #DateTable
values
('2017-01-01'),
('2017-01-02'),
('2017-01-03'),
('2017-01-04'),
('2017-01-05'),
('2017-01-06'),
('2017-01-07'),
('2017-01-08'),
('2017-01-09'),
('2017-01-10')
declare #SamleTable table (DateStamp date,Department nvarchar(50),LocationId nvarchar(50),Code int,name nvarchar(50),Entrada nvarchar(50))
insert into #SamleTable
values
('2017-01-01','BOTELLO','SANTO',5540,'JOSE','Something'),
('2017-01-04','BOTELLO','SANTO',5540,'JOSE','Something'),
('2017-01-06','BOTELLO','SANTO',5540,'JOSE','Something'),
('2017-01-09','BOTELLO','SANTO',5540,'JOSE','Something')
select z.Department,z.LocationId,z.Code,z.name,z.Dates,COALESCE(a.Entrada,'EMPTY') as Entrada from (
Select Department,LocationId,Code,Name,Dates from (
select Department,LocationId,Code,Name,MIN(DateStamp) mind, MAX(Datestamp) maxd from #SamleTable
group by Department,LocationId,Code,Name
)x
CROSS JOIN #DateTable b
where b.Dates between x.mind and x.maxd
) z
left join #SamleTable a on a.Department = z.Department and a.LocationId = z.LocationId and a.Code = z.Code and a.name = z.name
and a.DateStamp = z.Dates
Result
You can use a recursive query building all dates from the minimum date to the maximum date found in your table.
with dates(fecha, maxfecha) as
(
select min(fecha) as fecha, max(fecha) as maxfecha from vw_horarios
union all
select dateadd(dd, 1, fecha) as fecha, maxfecha from dates where fecha < maxfecha
)
select d.fecha, q.*
from dates d
left join ( your query here ) q on q.fecha = d.fecha;

How to optimize this query , it is taking time 40 minuts in execution?

select * from non_bidders_report_view
where applicant_category_id =1314
and applicant_status_id not in(10,11)
and partner_id = 4
and applicant_status_id <> 6
and applicant_id not in (
Select apb.applicant_id
from applicant_property_bids apb
inner join applicants a on
a.applicant_id=apb.applicant_id
where to_date(apb.bid_Date) >= to_date('30/4/2012','dd/mm/yyyy')
and to_date(apb.bid_Date) <= to_date('30/4/2015','dd/mm/yyyy')
and a.partner_id = 4 group by apb.applicant_Id
union
select aba.applicant_Id from Archive_Bid_Applicants aba
inner join applicants a on a.applicant_id=aba.applicant_id
where to_date(aba.bid_Date) >= to_date('30/4/2012','dd/mm/yyyy')
and to_date(aba.bid_Date) <= to_date('30/4/2015','dd/mm/yyyy')
and a.partner_id = 4 group by aba.applicant_Id
);
You can try this query:
select * from non_bidders_report_view nb
where applicant_category_id = 1314 and partner_id = 4
and applicant_status_id not in (6, 10, 11)
and not exists (
select 1 from applicant_property_bids abp
join applicants a on a.applicant_id=abp.applicant_id and a.partner_id=4
and abp.bid_Date between date '2012-04-30' and date '2015-04-30'
where abp.applicant_id = nb.applicant_id )
and not exists (
select 1 from archive_bid_applicants aba
join applicants a on a.applicant_id=aba.applicant_id and a.partner_id=4
and aba.bid_Date between date '2012-04-30' and date '2015-04-30'
where aba.applicant_id = nb.applicant_id )
The idea is to get rid of group by and union which seems to be unnecesary here and change not in to not exists.
Alternative solution:
select * from non_bidders_report_view nb
where applicant_category_id = 1314 and partner_id = 4
and applicant_status_id not in (6, 10, 11)
and not exists (
select 1 from (
select applicant_id, bid_date from applicant_property_bids
union all
select applicant_id, bid_date from archive_bid_applicants
) ab
join applicants a on a.applicant_id=ab.applicant_id and a.partner_id=4
and ab.bid_Date between date '2012-04-30' and date '2015-04-30'
where ab.applicant_id = nb.applicant_id )
If you have millions of data then create index on primary key. It will increase your performance. Indexes helps to speed up data retrieval. Create index on all 3 tables.

Select data for running total graph using Transact SQL

I have a table with following structure:
AuthorId, FollowersNumber, PublishDate, ...
What I need is to draw graph of a running total of FollowersNumber by periods. Tricky thing is that every author is counted only once. For example for following table:
AuthorId, FollowersNumber, PublishDate
1 100 '2013-01-01'
2 200 '2013-01-01'
3 200 '2013-01-02'
2 100 '2013-01-02'
4 60 '2013-01-03'
1 30 '2013-01-03'
Result must be:
2013-01-01 - 300 (100+200)
2013-01-02 - 500 (300+200)
2013-01-03 - 560 (500+60)
Now my SQL looks like (simplified):
SELECT 0, SUM (q.FollowersNumber) AS Y FROM
(SELECT FollowersNumber FROM dbo.Aggregate p WITH (NOLOCK) WHERE p.PublishDate BETWEEN #CurrentPeriod_0_Start AND #CurrentPeriod_0_End AND p.AuthorId not IN
(SELECT AuthorId FROM dbo.Aggregate WITH (NOLOCK) WHERE PublishDate BETWEEN #PreviousPeriod_0_Start AND #PreviousPeriod_0_End)) AS q
UNION
SELECT 1, SUM (q.FollowersNumber) AS Y FROM
(SELECT FollowersNumber FROM dbo.Aggregate p WITH (NOLOCK) WHERE p.PublishDate BETWEEN #CurrentPeriod_1_Start AND #CurrentPeriod_1_End AND p.AuthorId not IN
(SELECT AuthorId FROM dbo.Aggregate WITH (NOLOCK) WHERE PublishDate BETWEEN #PreviousPeriod_1_Start AND #PreviousPeriod_1_End)) AS q
etc.
After getting that data I count running total of FollowersNumber in C# code.
This query is monstrous and runs slow.
Is there any way to make it faster?
http://sqlfiddle.com/#!3/ff2cf/4
Get Totals history By dates. I don't understand your parameters in query
Possible this be helpful for you -
DECLARE #temp TABLE
(
AuthorId INT
, FollowersNumber INT
, PublishDate DATETIME
)
INSERT INTO #temp (AuthorId, FollowersNumber, PublishDate)
VALUES
(1, 100, '20130101'),
(2, 200, '20130101'),
(3, 200, '20130102'),
(2, 100, '20130102'),
(4, 60, '20130103'),
(1, 30, '20130103')
DECLARE
#CurrentPeriod_0_Start DATETIME
, #PreviousPeriod_0_Start DATETIME
, #CurrentPeriod_0_End DATETIME
, #PreviousPeriod_0_End DATETIME
, #CurrentPeriod_1_Start DATETIME
, #PreviousPeriod_1_Start DATETIME
, #CurrentPeriod_1_End DATETIME
, #PreviousPeriod_1_End DATETIME
SELECT 0, Y = SUM(p.FollowersNumber)
FROM #temp p
WHERE p.PublishDate BETWEEN #CurrentPeriod_0_Start AND #CurrentPeriod_0_End
AND NOT EXISTS(
SELECT 1
FROM #temp p2
WHERE p2.PublishDate BETWEEN #PreviousPeriod_0_Start AND #PreviousPeriod_0_End
AND p2.AuthorId = p.AuthorId
)
UNION ALL
SELECT 1, Y = SUM(p.FollowersNumber)
FROM #temp p
WHERE p.PublishDate BETWEEN #CurrentPeriod_1_Start AND #CurrentPeriod_1_End
AND NOT EXISTS(
SELECT 1
FROM #temp p2
WHERE p2.PublishDate BETWEEN #PreviousPeriod_1_Start AND #PreviousPeriod_1_End
AND p2.AuthorId = p.AuthorId
)
You can use this query instead of yours.
I wrote this query using CTE in SQL Server. I tested it on your data sample and worked fine.
WITH
RANKINGTABLE AS(
SELECT [AUTHORID]
,[FOLLOWERSNUMBER]
,[PUBLISHDATE]
,DENSE_RANK() OVER(PARTITION BY AUTHORID ORDER BY PUBLISHDATE) IRANK
FROM [TESTQUERY].[DBO].[AGGREGATE]
),
DAYSUM AS(
SELECT PUBLISHDATE, SUM([FOLLOWERSNUMBER]) DATESUM
FROM RANKINGTABLE
WHERE IRANK = 1
GROUP BY PUBLISHDATE
)
SELECT DS1.PUBLISHDATE,
(SELECT SUM(DS2.DATESUM) FROM DAYSUM DS2 WHERE DS2.PUBLISHDATE <= DS1.PUBLISHDATE) PTOTAL
FROM DAYSUM DS1
You can try this
SELECT 1, SUM(CASE WHEN PublishDate BETWEEN #PreviousPeriod_0_Start AND #PreviousPeriod_0_End
THEN NULL ELSE CASE WHEN PublishDate BETWEEN #CurrentPeriod_0_Start AND #CurrentPeriod_0_End
THEN FollowersNumber
END
END
) AS Y
FROM dbo.Aggregate WITH(NOLOCK)
UNION ALL
SELECT 0, SUM(CASE WHEN PublishDate BETWEEN #PreviousPeriod_1_Start AND #PreviousPeriod_1_End
THEN NULL ELSE CASE WHEN PublishDate BETWEEN #CurrentPeriod_1_Start AND #CurrentPeriod_1_End
THEN FollowersNumber
END
END
) AS Y
FROM dbo.Aggregate WITH(NOLOCK)
etc.

how do i get multiple records from 1 record

I have a product table with 15 fields like ItemID (primary),Name ,UPC,Price,Cost, etc.
Now I need to print labels the user can say
from Item "ABC" I need 15 labels
from item 'XYZ" I need 10 labels
I need a SQL statement which I will send the ItemID and the label Qty for Each record and it should give me back for each label a record for example 15 records for item "ABC" and 10 records for Item "XYZ" and so on
SELECT <fields>
FROM Mytable
Where Item = 'ABC'
GO 10
Will select those fields from that table 10 times in a row in 10 result sets.
Really though it sounds like you need to do what you are trying to do not in SQL, but in your calling application.
I agree this should be done on the client but if you insist, following duplicates each record 100 times and selects the amount you need from it.
;WITH ATable AS (
SELECT Item = 'ABC'
UNION ALL SELECT Item = 'XYZ'
)
, Temp (Item, Amount) AS (
SELECT 'ABC', 15
UNION ALL SELECT 'XYZ', 10
)
, q AS (
SELECT ID = 1
, Item
FROM ATable
UNION ALL
SELECT ID = q.ID +1
, q.Item
FROM q
WHERE ID < 100
)
SELECT q.*
FROM q
INNER JOIN Temp t ON t.Item = q.Item
AND t.Amount >= q.ID
You create the dynamic table aliased as r below. Works for amounts up to 2047.
select t.*
from
(select label='ABC', required=15 union all
select label='XYZ', required=10) r
inner join tbl t
on t.ItemID = r.label
inner join master..spt_values v
on v.type=Number and v.number between 1 and r.required
order by t.ItemID