Collating SQL query results - sql

I am having to write a query for an AS400 report. We are looking to group data by date. In other words we want to sum all data for each individual year and month. This is the query I have currently:
Select SCDATA.SCCLNT.CCLNT,
(Select SCDATA.SCCLNT.CNAME From SCDATA.SCCLNT
Where SCDATA.SCCLNT.CLTGRP Like 916500 Fetch First 1 Rows Only) As ClientName,
CONCAT(TRIM(SCDATA.SCCLNT.CADD1), SCDATA.SCCLNT.CADD2) As Address1,
CONCAT(TRIM(SCDATA.SCCLNT.CCITY), CONCAT(', ',
CONCAT(TRIM(SCDATA.SCCLNT.CSTATE), CONCAT(' ', TRIM(SCDATA.SCCLNT.CZIP)))))
As Address2,
SCDATA.SCCLNT.CLTGRP As Group,
SCDATA.SCPLHS.HMONTH || '-' || SCDATA.SCPLHS.HYEAR AS EndDate,
sum(HPL#) as Placed#,
sum (hpl$) as Placed$,
sum(HPMT$M) as PymtMth,
sum(HPMT$) as PymtTTL,
sum(HCOM$) as CommTTL,
sum(HPIF#) as PIF,
sum(HCLI#) as WithDrawn#,
sum(HCLI$) as WithDrawn$,
sum(HCLA#) as Closed#,
sum(HCLA$) as Closed$,
sum(HPMT$)/sum(HPL$) as Recovered,
sum(HAC#) as Active#,
sum(HAC$) as Active$
From SCDATA.SCCLNT
Inner Join SCDATA.SCPLHS On SCDATA.SCPLHS.HCLNT = SCDATA.SCCLNT.CCLNT And
(SCDATA.SCPLHS.HYEAR Between 17 And 17) and
(SCDATA.SCPLHS.HMONTH Between 01 And 10 )
Where SCDATA.SCCLNT.CLTGRP Like 916500
Group By SCDATA.SCPLHS.HYEAR ,
SCDATA.SCPLHS.HMONTH,
SCDATA.SCCLNT.CCLNT,
SCDATA.SCCLNT.CADD1,
SCDATA.SCCLNT.CADD2,
SCDATA.SCCLNT.CZIP,
SCDATA.SCCLNT.CLTGRP,
SCDATA.SCCLNT.CCITY,
SCDATA.SCCLNT.CSTATE
How can I collate this date so that my results show each date only once, and the sum of all data for that date?
Than you.
--EDIT--
Here are the results I am getting from the current query, tab delimited:
https://drive.google.com/open?id=0BwJ_JKr6NhYJVnNIVDcyNW9WMms CSV File
The results I need are:
https://drive.google.com/open?id=0BwJ_JKr6NhYJUTBDUTlDV00yanc

When I was grouping the query results I was including the SCCLNT column in the group. Each client group has multiple client numbers, which was causing the query to return multiple results, one for every client ID.

You statement has an aggregation problem - your GROUP BY includes two separate tables, the columns of which are not going to share an index (and quite possibly one of the tables may not have an index over the used columns at all). The statement is going to be slower than it needs to be.
You may find it more faster to do the aggregation just on the actual sale data:
SELECT Client.group, Client.name, Client.address1, Client.address2,
Historical.month || '-' || Historical.year as endDate
Historical.placed#, Historical.placed$,
Historical.pymtMth,
Historical.pymtTTL, Historical.commTTL,
Historical.PIF,
Historical.withdrawn#, Historical.withdrawn$,
Historical.closed#, Historical.closed$,
Historical.recovered,
Historical.active#, Historical.active$
FROM (SELECT SCPlHs.hYear as year,
SCPlHs.hMonth as month,
SUM(SCPlHs.hPl#) as placed#,
SUM(SCPlHs.hPl$) as placed$,
SUM(SCPlHs.hpmt$m) as pymtMth,
SUM(SCPlHs.hPmt$) as pymtTTL,
SUM(SCPlHs.hCom$) as commTTL,
SUM(SCPlHs.hPif#) as PIF,
SUM(SCPlHs.hCli#) as withdrawn#,
SUM(SCPlHs.hCli$) as withdrawn$,
SUM(SCPlHs.hCla#) as closed#,
SUM(SCPlHs.hCla$) as closed$,
SUM(SCPlHs.hPmt$) / SUM(SCPlHs.hpl$) as recovered,
SUM(SCPlHs.hAc#) as active#,
SUM(SCPlHs.hAc$) as active$
FROM SCData.SCPlHs
JOIN (SELECT DISTINCT cClnt as client
FROM SCData.SCClnt
WHERE SCClnt.cltGrp = 916500) Client
ON Client.client = SCPlHs.hClnt
-- dates, like all positive, contiguous-range types,
-- should be queries with an exclusive upper bound.
-- You should stop using BETWEEN, if possible.
WHERE SCPlHs.hYear >= 17 and SCPlHs.hYear < 18
AND SCPlHs.hMonth >= 1 and SCPlHs.hMonth < 11
GROUP BY SCPlHs.hYear, SCPlHs.hMonth) Historical
-- Cross joins multiply the total number of rows, but that's okay here because
-- the joined table is going to only have one row
CROSS JOIN (SELECT SCClnt.cltGrp as group
SCClnt.cName as name,
TRIM(SCClnt.cAdd1) || TRIM(SCClnt.cAdd2) as address1,
TRIM(SCClnt.cCity) || ', ' || TRIM(SCClnt.cState) || ' ' || TRIM(SCClnt.cZip) as address2
FROM SCData.SCClnt
WHERE SCClnt.cltGrp = 916500
FETCH FIRST 1 ROW ONLY) Client
ORDER BY Historical.year, Historical.month

Related

SQL Server: Generate squashed range data from daily dates by an id

Basically to sum up the goal. I want to generate something using the following data. Where it subtotals like excel where "each change in ... ordered however" generate summary records and only generate summary records and squash date ranges.
All I want are the count records would generate rows and would copy the fee data except for the begin/end fields would be the ranged dates. So first record would be fee 9858 from 3/31 - 4/14 for example. Each count row would be a new fee record.
I am not sure which combination of grouping, partition by.... and such to get what I need or if there is something else I can use. I can provide sql if needed but I am mainly looking for finding the right combination of tools(partition by, grouping, rollup....) that would provide this functionality.
WITH [ag]
AS (SELECT *,
LAG([Fee_ID]) OVER (ORDER BY [FeeTypeID], [FeeBeginDate]) [FirstFee],
LAG([Fee_ID]) OVER (ORDER BY [FeeTypeID], [FeeBeginDate] DESC) [LastFee]
FROM [dbo].[HHTFees]
WHERE [Retailer] = 517),
[agf]
AS (SELECT *,
'Beg' [FeeStopType]
FROM [ag]
WHERE [ag].[FirstFee] <> [ag].[Fee_ID]
OR [ag].[FirstFee] IS NULL),
[agl]
AS (SELECT *,
'End' [FeeStopType]
FROM [ag]
WHERE [ag].[LastFee] <> [ag].[Fee_ID]
OR [ag].[LastFee] IS NULL),
[results]
AS (SELECT *
FROM [agf]
UNION
SELECT *
FROM [agl]),
[indexed]
AS (SELECT *,
ROW_NUMBER() OVER (ORDER BY [results].[FeeTypeID], [results].[FeeBeginDate]) [RowNum]
FROM [results])
SELECT [Starts].[Retailer],
[Starts].[Chain_key],
[Starts].[Fee_ID],
[Starts].[FeeTypeID],
[Starts].[FeeChgTypeID],
[Starts].[Fee],
[Starts].[FeeDescription],
[Starts].[FeeTypeDescription],
[Starts].[FeeBeginDate],
[Ends].[FeeEndDate],
[Starts].[FeeAmt],
[Starts].[HHT_ID],
[Starts].[CreatedBy],
[Starts].[CreatedDate],
[Starts].[ModifiedBy],
[Starts].[ModifiedDate],
[Starts].[MsgID],
[Starts].[ScopeOrder],
[Starts].[Scope],
[Starts].[FirstFee],
[Starts].[LastFee],
[Starts].[FeeStopType],
[Starts].[RowNum]
FROM [indexed] [Starts]
INNER JOIN [indexed] [Ends]
ON [Starts].[Fee_ID] = [Ends].[Fee_ID]
AND [Ends].[RowNum] = [Starts].[RowNum] + 1;
I used lag to find where the fee id changed sorted by feetype / start date. This allowed me to mimic when fee id changed sorted by begin date like excel subtotal. Now to optimize and tweak to fit the set of data.

Selecting the last set of records in SQL Server that match certain criteria

I've got 10 records that match the criteria I'm searching for. The problem is there are two sets of 10 records, one at 1pm and one at 3pm. I only want the sets at 3pm. Here's part of my SQL:
select shp_rev.ShpNum, shp_rev.RevTime
from shp_rev
where shp_rev.RevDate = '10/1/2015'
and shp_rev.ValAfter = 'O'
and shp_rev.ShpNum = 732809
(I've added the shp_rev.ShpNum to the where just to narrow down the data to a dataset that has this problem. Normally I wouldn't have that in the where. And there are other fields that would be included with this select.)
This produces:
732809 13:14:45
732809 13:14:45
...
732809 15:23:33
732809 15:23:33
...
I only want the records at 15:23:33. I know one way I can do it is to concatenate all of the fields I want into one string with ShpNum and RevTime at the beginning then using MAX() to get just the 3pm records like this:
select max(cast(shp_rev.ShpNum as varchar) + '~' + shp_rev.RevTime)
from shp_rev
where shp_rev.RevDate = '10/1/2015'
and shp_rev.ValAfter = 'O'
and shp_rev.ShpNum = 732809
order by max(cast(shp_rev.ShpNum as varchar) + '~' + shp_rev.RevTime)
But then I have to parse back that string to get everything. It seems there must be a better way. I've tried using MAX() on ShpNum and RevTime separately but that doesn't work. Any thoughts? Oh, I'm working in SQL Server 2012. Thanks!
If I understand correctly, you can use dense_rank():
select s.*
from (select shp_rev.ShpNum, shp_rev.RevTime,
dense_rank() over (partition by revdate, valafter, shipnum order by revtime desc) as seqnum
from shp_rev
where shp_rev.RevDate = '2015-10-01' and
shp_rev.ValAfter = 'O' and
shp_rev.ShpNum = 732809
) s
where seqnum = 1;
This assumes that the time stamps are all exactly the same.

Oracle Query on Comma Separated Values In a Column

I have two tables. One table is a list of access points. The other is a list of who has access to what.
It use to be a person had access to either one thing or all things. But, now that has changed. Now someone might have access to several points.
The wrench in the system is that in the column that shows what they have access to may have a single value, "ALL" for all access or a comma separated list (which is new).
I originally thought I could just do WHERE Access_To Here IN(), but I am unsure how to convert the value of Access_To to a formatted list.
I need to be able to do this as a single query since I am using it for a LOV in APEX.
So, I need some help.
The Access_Points_Table has only one column, Access_Points. Here are some example values that it might have:
CSX
CZR
XR3
NBO
QHG
The Users_List table has several columns, but the most important are User_Name, Access_To. Here are some example values that it might have:
Joe | ALL
Fred | CSX
Allen | CZR, NBO
Hank | QHG
Here is query I am currently using, but it only works if there is only a single value in the Access_To column.
SELECT DISTINCT Access_Points VALUE
FROM Access_Points_Table apt
JOIN
Users_List ul
ON (ul.wwid = 'ZZ999'
AND (ul.Access_To = 'ALL' OR apt.symbol_name = ul.Access_To))
ORDER BY name ASC
What I am trying to accomplish is:
SELECT DISTINCT Access_Points VALUE
FROM Access_Points_Table apt
JOIN
Users_List ul
ON (ul.wwid = 'ZZ999'
AND (ul.Access_To = 'ALL' OR apt.symbol_name IN(Something Goes Here)))
ORDER BY name ASC
So, if run a query for the user Allen, it will return the rows:
CZR
NBO
Depending on the length of your comma-delimited data, you can use Oracle's regular expression engine (the difficulty is that Oracle limits regular expressions to 512 bytes):
SELECT DISTINCT Access_Points VALUE
FROM Access_Points_Table apt
JOIN Users_List ul
ON ( ul.wwid = 'ZZ999'
AND ( ul.Access_To = 'ALL'
OR REGEXP_LIKE(apt.symbol_name, '^(' || REPLACE(ul.Access_To, ',', '|') || ')$') ) )
ORDER BY name ASC
Alternately you can use LIKE:
SELECT DISTINCT Access_Points VALUE
FROM Access_Points_Table apt
JOIN Users_List ul
ON ( ul.wwid = 'ZZ999'
AND ( ul.Access_To = 'ALL'
OR ',' || ul.Access_To || ',' LIKE '%,' || apt.symbol_name || ',%' ) )
ORDER BY name ASC
Note that if Access_To has spaces after its commas as in your OP, it does add some complexity but that can be overcome, simply REPLACE(ul.Access_To, ' ').
By the way, I do wonder why this: ul.wwid = 'ZZ999' is in the ON clause instead of in a WHERE clause.
Another way would be to use the instr test:
SELECT DISTINCT access_points VALUE
FROM access_points_table a
JOIN users_list u
ON ( INSTR(u.access_to,a.access_points,1) > 0 )
ORDER BY 1

string aggregation and joining with another table column

I have following tables:
weighment_tran
village_cd
farmer_id
registered_farmer_id
plot_no
out_date
net_wt
village_dir
village_cd
village_name
taluka_cd
district_cd
taluka_dir
taluka_cd
taluka_name
district_dir
district_cd
district_name
farmer_dir
farmer_id
first_name
middle_name
agreement_tran
farmer_id
registered_farmer_id
payment_farmer_id
plot_no
main_sy_no
payment_bank_cd
payment_account_no
bank_dir
bank_cd
bank_name
bank_branch
Mainly I have two transaction tables namely agreement_tran (agreements are stored) and weighment_tran (weighment of products stored only those present in agreement_tran) and others are directories those holds lookup for actual names for codes like bank_cd looks for actual bank_name in bank_dir. farmer_id, registered_farmer_id, payment_farmer_id are of same column values. What I need is as below with out_date range:
Sl.No farmer_name Sy_nos village taluka district payment_farmer_id payment_account_no bank_name bank_branch sum(net_wt)
Every plot_no has a sy_no, I need to concatenate all sy_nos as I am selecting sum(net_wt) as net_wt concerned to every plot_no.
I tried something like-
select row_number() over (order by a.payment_farmer_id),
a.payment_farmer_id,
(select f.first_name ||' '|| f.middle_name as name
from farmer_dir f
where a.payment_farmer_id=f.farmer_id),
(select wm_concat(main_sy_no) from agreement_tran a
where a.plot_no=w.plot_no),
(select sum(net_wt)
from weighment_tran w
where (w.plot_no = a.plot_no)
and (w.season_cd = 9) and trunc(w.out_date) between
to_date('22-12-2013','dd-mm-yyyy') and to_date('23-12-2013','dd-mm-yyyy')
group by a.payment_farmer_id)
from agreement_tran a
but didn't get what I want.
There seem to be some mistakes in your query:
First subquery looks okay.
Second subquery is wrong. You select agreement_tran a second time but use the same alias. Moreover you compare with w.plot_no, but there is no w table. (I suppose you meant to name the inner agreement_tran w?)
In your second subquery the group by clause makes no sense. Remove it.
Is it on purpose that you don't sort your results? Above you number your lines, but at the end of the query you have no order by, which may result in a random order.
Your query shows all agreements, their farmer's name, all sy nos of all agreements with the same plot number, and the sum of all net weights with the same plot number in a certain time slot. Please check if this is meets your expectations. Do you really want to select all Agreements or rather all plots for instance?
SELECT Sum(WEIGHMENT_TRAN.NET_WT),
AGREEMENT_TRAN.PAYMENT_FARMER_ID,
wm_concat(Distinct (agreement_tran.main_sy_no)) as Sy_no,
wm_concat(Distinct (village_dir.village_name)) as Village,
farmer_dir.first_name || ' ' || farmer_dir.middle_name as Farmer_name, taluka_dir.taluka_name, district_dir.district_name, bank_dir.bank_name, bank_dir.bank_branch, agreement_tran.payment_account_no, weighment_tran.registered_farmer_id
FROM AGRI.AGREEMENT_TRAN AGREEMENT_TRAN,
AGRI.village_dir village_dir,
AGRI.WEIGHMENT_TRAN WEIGHMENT_TRAN,
agri.farmer_dir farmer_dir, agri.taluka_dir taluka_dir,
agri.district_dir district_dir, agri.bank_dir bank_dir
WHERE AGREEMENT_TRAN.PLOT_NO = WEIGHMENT_TRAN.PLOT_NO AND
((AGREEMENT_TRAN.SEASON_CD=9) AND (WEIGHMENT_TRAN.SEASON_CD=9) AND
(trunc(weighment_tran.out_date) Between to_date('22-12-2013','dd-mm-yyyy')
And to_date('22-12-2013','dd-mm-yyyy')) AND
(AGREEMENT_TRAN.PLOT_NO=weighment_tran.plot_no) AND (WEIGHMENT_TRAN.VILLAGE_CD=village_dir.village_cd)) and agreement_tran.payment_farmer_id=farmer_dir.farmer_id and village_dir.taluka_cd=taluka_dir.taluka_cd and village_dir.district_cd=district_dir.district_cd and agreement_tran.payment_bank_cd=bank_dir.bank_cd
GROUP BY AGREEMENT_TRAN.PAYMENT_FARMER_ID, farmer_dir.first_name, farmer_dir.middle_name, taluka_dir.taluka_name, district_dir.district_name,bank_dir.bank_name, bank_dir.bank_branch, agreement_tran.payment_account_no, weighment_tran.registered_farmer_id
Order by bank_dir.bank_name, bank_dir.bank_branch
finally I rewarded for my long effort and thanking #Thorsten for his time n patience.

SQL Query - Distinct doesn't seem to filter

I'm utilizing four separate tables and i can't seem to figure out why my DISTINCT isn't filtering the results. I'm trying to get a single result for each acct.Name in this query. Regardless if i use DISTINCT or not, i get the exact same results.
Select DISTINCT
acct.Name,
inv.InvoiceNumber,
acct.AccountNumber,
addr.Line1,
addr.Line2,
addr.Line3,
addr.City,
addr.StateOrProvince,
addr.postalcode
FROM InvoiceBase inv, AccountBase acct
JOIN AccountExtensionBase base
ON base.AccountId = acct.AccountId
JOIN CustomerAddressBase addr
ON addr.ParentId = acct.AccountId
WHERE
inv.AccountId=acct.AccountId And
base.New_cocat_master = 1 And
base.New_CompanyId = 1 And
inv.StateCode = 0 And
inv.Name = '2013 ' + acct.AccountNumber
ORDER by acct.Name
The first result i get now has the first three values (acct.Name, inv.InvoiceNumnber, acct.AccountNumber) and the rest of the columns are blank. The second row has all of the columns with the information. I'm just trying to make the acct.Name to be DISTINCT
The rows are DISTINCT
This may be confusing when you are selecting multiple strings, since there might be hidden characters/spaces. Select the length of each one of those fields and compare the so called duplicate rows.
Turns out all i had to do was add in a simple clause in the WHERE, since the address is required for a valid invoice (where to send it):
WHERE
base.New_cocat_master = 1 And
base.New_CompanyId = 1 And
inv.StateCode = 0 And
inv.Name = '2013 ' + acct.AccountNumber And
addr.Line1 IS NOT NULL