SQL Single Row query advice - sql

Can i please get some help with my SQL report query, i am 90% there just need the last step (still learning SQL so be kind :) ).
We currently have 2 different databases:
- [DATABASE1] stores all our assets
- [DATABASE2] stores all assets that we are still paying off
- - This database stores every payment made against the asset to the bank, the date the payment was made, the amount etc.
- - The last row against the asset will be the last payment, and the date on this would be the expected lease end date.
I would like a report that will have a single line per asset shows all columns.
My current report shows all the required information, however it shows ALL the rows per asset instead of a single encapsulated row, e.g:
ASSET NO | FINANCIER | AGEEMENT NUMBER | PAYMENT NUMBER | LEASE COMMENCE DATE | LEASE FINAL DATE | MONTHLY PAYMENTS
asset1 | bank 1 | 1111 | 1 | 01/01/2017 | NULL | NULL
asset1 | bank 1 | 1111 | 2 | NULL | NULL | 2000
asset1 | bank 1 | 1111 | 3 | NULL | NULL | NULL
..
asset1 | bank 1 | 1111 | 20 | NULL | 01/01/2020 | NULL
asset2 | bank 5 | 1536 | 1 | 05/08/2016 | NULL | NULL
..
Instead of:
ASSET NO | FINANCIER | AGEEMENT NUMBER | PAYMENT NUMBER | LEASE COMMENCE DATE | LEASE FINAL DATE | MONTHLY PAYMENTS
asset1 | bank 1 | 1111 | 20 | 01/01/2017 | 01/01/2020 | 2000
asset2 | bank 5 | 1536 | 15 | 05/08/2016 | 12/05/2019 | 5500
..
Below is my query:
Declare #MaxPays TABLE (
ITEMNO VARCHAR(MAX),
PAYNO VARCHAR(MAX)
)
INSERT INTO #MaxPays
SELECT
a.ITEMNO,
a.PAYNO
FROM
[DATABASE1] a
INNER JOIN
(SELECT ITEMNO, MAX(PAYNO) as PAYNO FROM [DATABASE1] GROUP BY ITEMNO) AS b ON
a.ITEMNO = b.ITEMNO AND a.PAYNO = b.PAYNO
SELECT
a.ITEMNO as 'Asset #',
a.FINANCE as 'Financier',
a.AGREENO as 'Agreement number',
a.PAYNO as 'Payment Number',
CASE WHEN a.PAYNO = 1 THEN a.PAYDATE ELSE NULL END as 'Lease Commencing Date',
CASE WHEN a.PAYNO = (SELECT PAYNO FROM #MaxPays WHERE ITEMNO = a.ITEMNO) THEN a.PAYDATE ELSE NULL END as 'Lease Finalising Date',
CASE WHEN a.PAYNO = 2 THEN a.PAYAMOUNT ELSE NULL END as 'Monthly Payments'
FROM
[DATABASE1] a
INNER JOIN
(SELECT DISTINCT ITEMNO from [DATABASE2]) AS b ON
a.ITEMNO = b.ITEMNO
ORDER BY a.ITEMNO
EDIT: The monthly payment links to the 2nd instance because sometimes the 1st payment includes down payments, and isnt a clear indicator of the recurring monthly payments
Any help would be appreciated.
Thanks

You just need a minor modification in your query -
SELECT
a.ITEMNO as 'Asset #',
a.FINANCE as 'Financier',
a.AGREENO as 'Agreement number',
a.PAYNO as 'Payment Number',
MAX(CASE WHEN a.PAYNO = 1 THEN a.PAYDATE ELSE NULL END) as 'Lease Commencing Date',
MAX(CASE WHEN a.PAYNO = (SELECT PAYNO FROM #MaxPays WHERE ITEMNO = a.ITEMNO) THEN a.PAYDATE ELSE NULL END) as 'Lease Finalising Date',
MAX(CASE WHEN a.PAYNO = 2 THEN a.PAYAMOUNT ELSE NULL END) as 'Monthly Payments'
FROM
[DATABASE1] a
INNER JOIN
(SELECT DISTINCT ITEMNO from [DATABASE2]) AS b ON
a.ITEMNO = b.ITEMNO
GROUP BY a.ITEMNO,
a.FINANCE,
a.AGREENO,
a.PAYNO,
ORDER BY a.ITEMNO

Related

new vs old customers sql

I am trying to get new customer's vs returning customers and for this I have to create multiple tables. Is there a better way to aggregate the data shown like below:
my SQL code looks like below:
---- ALL INDIVIDUALS WHO PURCHASED IN CURRENT WEEK---------
CREATE TABLE PURCHASES_FEB_WK2 AS (Select DISTINCT INDIVIDUAL_ID
from DM_OWNER.TRANSACTION_DETAIL_MV
WHERE BRAND_ORG_CODE = 'BRAND'
and is_merch = 1
and currency_code = 'USD'
AND LINE_ITEM_AMT_TYPE_CD = 'S'
AND TRUNC(TXN_DATE) BETWEEN '10-FEB-19' AND '16-FEB-19')
----------MINIMUM PURCHASE DATE OF ALL CUSTOMERS------------
Create table feb_wk2_min as
Select distinct Individual_ID, MIN(TRANSACTION_DATE) as FIRST_TRANSACTION
from dm_owner.transaction_mv
WHERE BRAND_ORG_CODE = 'BRAND'
and transaction_type_code in ('PR','EP')
group by individual_ID;
------- NEW CUSTOMERS FOR THE WEEK---------
Select Count(distinct B.INDIVIDUAL_ID)
from PURCHASES_FEB_WK2 A
JOIN FEB_WK2_MIN B ON A.INDIVIDUAL_ID = B.INDIVIDUAL_ID
where FIRST_TRANSACTION between '10-FEB-19' and '16-FEB-19'
---- ALL RETURNING CUSTOMERS
SELECT COUNT (DISTINCT INDIVIDUAL_ID)
FROM PURCHASES_FEB_WK2
WHERE INDIVIDUAL_ID IN (SELECT INDIVIDUAL_ID FROM DM_OWNER.TRANSACTION_DETAIL_MV WHERE TRUNC(TXN_DATE) < '10-FEB-19' AND BRAND_ORG_CODE = 'BRAND' AND IS_MERCH = 1 AND line_item_amt_type_cd = 'S' AND STATUS = 'A')
-------NEW CUSTOMERS DOLLAR_VALUE_US------
SELECT SUM(DOLLAR_VALUE_US) FROM DM_OWNER.TRANSACTION_DETAIL_MV
WHERE INDIVIDUAL_ID IN (Select distinct B.INDIVIDUAL_ID
from PURCHASES_FEB_WK2 A
JOIN FEB_WK2_MIN B ON A.INDIVIDUAL_ID = B.INDIVIDUAL_ID
where FIRST_TRANSACTION between '10-FEB-19' and '16-FEB-19')
AND BRAND_ORG_CODE = 'BRAND'
and is_merch = 1
and currency_code = 'USD'
AND LINE_ITEM_AMT_TYPE_CD = 'S'
AND TRUNC(TXN_DATE) BETWEEN '10-FEB-19' AND '16-FEB-19'
-------RETURNING CUSTOMERS DOLLAR_VALUE_US------
SELECT SUM(DOLLAR_VALUE_US) FROM DM_OWNER.TRANSACTION_DETAIL_MV
WHERE INDIVIDUAL_ID IN (SELECT DISTINCT INDIVIDUAL_ID
FROM PURCHASES_FEB_WK2
WHERE INDIVIDUAL_ID IN (SELECT INDIVIDUAL_ID FROM DM_OWNER.TRANSACTION_DETAIL_MV WHERE TRUNC(TXN_DATE) < '10-FEB-19' AND BRAND_ORG_CODE = 'BRAND' AND IS_MERCH = 1 AND line_item_amt_type_cd = 'S' AND STATUS = 'A'))
AND BRAND_ORG_CODE = 'BRAND'
and is_merch = 1
and currency_code = 'USD'
AND LINE_ITEM_AMT_TYPE_CD = 'S'
AND TRUNC(TXN_DATE) BETWEEN '10-FEB-19' AND '16-FEB-19'
To get the quantity and the count of order, I am replacing the sum (dollar_value_us) with count of distinct orders and sum of quantity. Is there an easy way to pivot and combine this code so that I can just copy paste the data in the format (picture attached) I have provided.
Based on the comments, I understand that you want to split the customers into two groups : customers that had their first transactions during the period should be separated from thoses who had transactions before. For each group, you want to count the number of customers and sum the value of the transactions.
NB : your sql code does not show hot to compute qty and count_of_orders, so I left it apart (but this will likely follow the same logic).
Given this sample data:
INDIVIDUAL_ID | DOLLAR_VALUE_US | TXN_DATE | RAND_ORG_CODE | IS_MERCH | CURRENCY_CODE | LINE_ITEM_AMT_TYPE_CD
------------: | --------------: | :-------- | :------------ | -------: | :------------ | :--------------------
1 | 10 | 01-FEB-19 | BRAND | 1 | USD | S
1 | 10 | 10-FEB-19 | BRAND | 1 | USD | S
1 | 10 | 15-FEB-19 | BRAND | 1 | USD | S
1 | 10 | 28-FEB-19 | BRAND | 1 | USD | S
2 | 11 | 11-FEB-19 | BRAND | 1 | USD | S
2 | 11 | 12-FEB-19 | BRAND | 1 | USD | S
3 | 11 | 12-FEB-19 | BRAND | 1 | USD | S
Considering week range from February 10th to 16th included, customer 1 is a returning customer with 2 transactions in the window, and customers 2 and 3 are new customers with respectively 2 and 1 transactions. You would expect the following output:
TYPE_OF_CUSTOMER | COUNT_OF_CUSTOMERS | SUM_DOLLAR_VALUE_US
:------------------ | -----------------: | ------------------:
New Customers | 2 | 33
Returning Customers | 1 | 20
To solve this, you need to set up several levels of aggregation. First, use window function MIN() OVER() to recover the date of the first transaction of each customer. Then, filter on the anlaysis period, split customers into new/returning groups, and aggregate the money spent. Finally, aggregate all results together.
Query:
SELECT
DECODE(is_new, 1, 'New Customers', 'Returning Customers') type_of_customer,
COUNT(individual_id) count_of_customers,
SUM(dollar_value_us) sum_dollar_value_us
FROM (
SELECT
individual_id,
SUM(dollar_value_us) dollar_value_us,
CASE WHEN MIN(txn_date) = min_txn_date THEN 1 ELSE 0 END is_new
FROM (
SELECT
individual_id,
dollar_value_us,
txn_date,
MIN(txn_date) OVER(PARTITION BY individual_id) min_txn_date
FROM transaction_detail_mv
WHERE
rand_org_code = 'BRAND'
AND is_merch = 1
AND currency_code = 'USD'
AND line_item_amt_type_cd = 'S'
) t
WHERE
txn_date >= TO_DATE('10-02-2019', 'DD-MM-YYYY')
AND txn_date < TO_DATE('17-02-2019', 'DD-MM-YYYY')
GROUP BY
individual_id,
min_txn_date
) x GROUP BY is_new
This demo on DB Fiddle demonstrates each step of the computation.

Should search whole table and give data in one row according to the condition

I have a table with values as follows:
AC_NO INV_NO CC FT
--------------------------------
1 A PTN INVOICE
1 A PTN BDE
2 B ABC INVOICE
2 B PTN INVOICE
2 B PTN BDE
SELECT
ac_no,
CASE
WHEN FT like '%INVOICE%' THEN 'AVAILABLE' else 'NOTAVAILABLE'
END AS INVOICE ,
CASE
WHEN FT like '%BDE%' THEN 'AVAILABLE' else 'NOTAVAILABLE'
END AS BDE
FROM Account_info where CC='PTN';
Output
------------------------------------
1 A Available Notavailable
1 A Notavailable Available
2 B Available Notavailable
2 B Notavailable Available
but I need them in the following format:
AC_NO INVOICE BDE
--------------------------------
1 AVAILABLE AVAILABLE
2 AVAILABLE AVAILABLE
Use MAX ( CASE ) and GROUP BY along with COALESCE or NVL
SQL Fiddle
Query 1:
SELECT ac_no,
COALESCE (MAX (CASE WHEN FT = 'INVOICE' THEN 'AVAILABLE' END),
'NOTAVAILABLE')
AS INVOICE,
COALESCE (MAX (CASE WHEN FT = 'BDE' THEN 'AVAILABLE' END),
'NOTAVAILABLE')
AS BDE
FROM Account_info
WHERE CC = 'PTN'
GROUP BY ac_no
Results:
| AC_NO | INVOICE | BDE |
|-------|-----------|-----------|
| 1 | AVAILABLE | AVAILABLE |
| 2 | AVAILABLE | AVAILABLE |

SQL how to join two tables without duplicate

I have these two tables. I am trying to get the amount in the item_tbl, but the payment_dt is in another table (payment_tbl).
How can I join the two tables and get the amount correctly? Right now, my SQL (using PostgreSQL) generates 3 rows so the amount is multiplied by three when I get the sum.
item_tbl:
receipt_no | gross_amount | other_discount_amount | net_of_discount_amount
0000000617 | 2000.00 | 400.00 | 1600.00
payment_tbl:
receipt_no | amount(net) | payment_method | payment_dt
0000000617 | 639.49 | cash | 2016-05-31 11:48:23.5+08
0000000617 | 500.00 | check | 2016-05-31 11:48:23.5+08
0000000617 | 500.00 | debit card | 2016-05-31 11:48:23.5+08
expected result:
gross_amount | other_discount_amount | net_of_discount_amount
2000.00 | 400.00 | 1600.00
Query:
SELECT
cashier.cashier_name,
COALESCE(gross_amount, 0) AS gross_amount,
(CASE WHEN item.other_discount_type = 'OTHER' THEN COALESCE(item.other_discount_amount, 0) ELSE 0 END) AS other_discount_amount,
COALESCE(item.net_of_discount_amount, 0) AS net_of_discount_amount
FROM
item_tbl item
INNER JOIN
payment_tbl payment ON item.receipt_no = payment.receipt_no
LEFT JOIN
cashier_tbl cashier ON cashier.id = item.cashier_id
WHERE
date(payment.payment_dt) = to_date('31 May 2016', 'dd Mon YYYY')
All payment records for a particular receipt must have the same date time for this to work correctly.
SELECT
cashier.cashier_name,
COALESCE(gross_amount, 0) AS gross_amount,
(CASE WHEN item.other_discount_type = 'PATRONAGE_CASH' THEN COALESCE(item.other_discount_amount, 0) ELSE 0 END) AS other_discount_amount,
COALESCE(item.net_of_discount_amount, 0) AS net_of_discount_amount
FROM item_tbl item
INNER JOIN (Select receipt_no, Max(payment_dt) payment_dt from payment_tbl Group By receipt_no) payment ON item.receipt_no = payment.receipt_no
LEFT JOIN cashier_tbl cashier ON cashier.id = item.cashier_id
WHERE
AND date(payment.payment_dt) = to_date('31 May 2016', 'dd Mon YYYY')

Multiple sql query or Cursor?

I need help on something that seems to be complex to me.
I made a query to create a tbl1 which is the Cartesian product of the tables Item and Warehouse. It give’s me back all items in all warehouses:
SELECT i.ItemID, w.WarehouseID
FROM Item i, Warehouse w
I made a second query (tbl2) where I check the date of the last document previous or equal to a variable date (#datevar) and whose quantity rule is 1 (PhysicalQtyRule = 1), this by Item and Warehouse, obtained from StockHistory table
SELECT MAX(CreateDate) AS [DATE1], ItemID, Quantity, WarehouseID
FROM StockHistory
WHERE PhysicalQtyRule = 1 AND CreateDate <= #datevar
GROUP BY ItemID, Quantity, WarehouseID
Now, I need more three steps:
Build a third table containing per item and warehouse the sum of quantity, but the quantity rule is 2 (PhysicalQtyRule = 2) and date between tbl2.date (if exists) and the date of the variable #datevar, obtained from the table StockHistory. Something like that:
SELECT ItemID, WarehouseID, SUM(Quantity)
FROM StockHistory
WHERE PhysicalQtyRule = 2 AND CreateDate > tbl2.DATE1 --If exists
AND CreateDate <= #datevar
GROUP BY ItemID, WarehouseID
Build a fourth table containing per item and warehouse the sum of quantity, but the quantity rule is 3 (PhysicalQtyRule = 3) and date between tbl2.date (if any) and the date of the variable #datevar, obtained from the table StockHistory. Something like that:
SELECT ItemID, WarehouseID, SUM(Quantity)
FROM StockHistory
WHERE PhysicalQtyRule = 3 AND CreateDate > tbl2.DATE1 --If exists
AND CreateDate <= #datevar
GROUP BY ItemID, WarehouseID
Create a final table based on the first one, with an sum quantity column, something like that:
SELECT i.ItemID, w.WarehouseID, tbl2.Quantity + tbl3.Quantity – tbl4.Quantity AS [Qty]
FROM Item i, Warehouse w
I don't know if need cursors (something new for me) or multiple querys, but it's important the best performance because my StockHistory table have millions of records.
Can anyone help-me please? Thank you!
Some sample data, only for one Item and one warehouse:
+--------+-------------+------------+-----------------+----------+
| ItemID | WarehouseID | CreateDate | PhysicalQtyRule | Quantity | Balance | comments
+--------+-------------+------------+-----------------+----------+
| 1234 | 11 | 2013-03-25 | 2 | 35 | 35 | Rule 2 = In
| 1234 | 11 | 2013-03-28 | 3 | 30 | 5 | Rule 3 = Out
| 1234 | 11 | 2013-04-01 | 1 | 3 | 3 | Rule 1 = Reset
| 1234 | 11 | 2013-07-12 | 2 | 40 | 43 | Rule 2 = In
| 1234 | 11 | 2013-09-05 | 3 | 20 | 23 | Rule 3 = Out
| 1234 | 11 | 2013-12-31 | 1 | 25 | 25 | Rule 1 = Reset
| 1234 | 11 | 2014-01-09 | 3 | 11 | 14 | Rule 3 = Out
| 1234 | 11 | 2014-01-16 | 3 | 6 | 8 | Rule 3 = Out
I want to know the balance on any variable date.
Without your data, I can't test this but I believe this should be your solution.
SELECT i.ItemID
,w.WarehouseID
,[Qty] = tbl2.Quantity + tbl3.Quantity – tbl4.Quantity
FROM Item i
CROSS JOIN Warehouse w
OUTER APPLY (
SELECT [DATE1] = MAX(sh.CreateDate)
,sh.ItemID
,sh.Quantity
,sh.WarehouseID
FROM StockHistory sh
WHERE sh.PhysicalQtyRule = 1 AND sh.CreateDate <= #datevar
AND i.ItemID = sh.ItemID
AND w.WarehouseID = sh.WarehouseID
GROUP BY sh.ItemID, sh.Quantity, sh.WarehouseID ) tbl2
OUTER APPLY (
SELECT sh.ItemID
,sh.WarehouseID
,[Quantity] = SUM(sh.Quantity)
FROM StockHistory sh
WHERE sh.PhysicalQtyRule = 2 AND sh.CreateDate > tbl2.DATE1 --If exists
AND sh.CreateDate <= #datevar AND i.ItemID = sh.ItemID
AND w.WarehouseID = sh.WarehouseID
GROUP BY sh.ItemID, sh.WarehouseID ) tbl3
OUTER APPLY (
SELECT sh.ItemID
,sh.WarehouseID
,[Quantity] = SUM(sh.Quantity)
FROM StockHistory sh
WHERE sh.PhysicalQtyRule = 3 AND sh.CreateDate > tbl2.DATE1 --If exists
AND sh.CreateDate <= #datevar AND i.ItemID = sh.ItemID
AND w.WarehouseID = sh.WarehouseID
GROUP BY sh.ItemID, sh.WarehouseID ) tbl4

Select distinct records with Min Date from two tables with Left Join

I'm trying to retrieve all distinct AccountId’s as well as the earliest InsertDate for each. Occasionally the AccountId is not known and although the transactions may be distinct I want to bucket all of the ‘-1’s into their own group.
This is what I have attempted so far along with the schemas.
CREATE TABLE #tmpResults (
Trans Varchar(12),
AccountId Varchar(50),
EarlyDate DateTime DEFAULT getdate(), CardType Varchar(16))
insert #tmpResults
select [Trans] = convert(varchar(12),'CashSale')
, [AccountId] = b.AccountId
, [EarlyDate] = min(b.InsertDate)
, case when c.name LIKE '%VISA%' then 'VISA'
when c.name LIKE '%MasterCard%' then 'MasterCard'
when c.name LIKE '%AMEX%' then 'AMEX'
else 'Other'
end as [CardType]
from TransBatch b
left join CardVer_3 c WITH (NOLOCK) ON c.Id = B.BatchId
left join TransBatch b2
on (b.accountid = b2.accountid and (b.InsertDate > b2.InsertDate or b.InsertDate = b2.InsertDate))
and b2.accountid is NULL
group by b.accountid, b.InsertDate,c.name
order by b.accountid DESC
select * from #tmpResults
The table schemas are like so:
**TransBatch**
RecordId |BatchId |InsertDate | AccountId | AccNameHolder
6676 | 11 | 2012-11-01 05:19:04.000 | 12345 | Account1
6677 | 11 | 2012-11-01 05:19:04.000 | 12345 | Account1
6678 | 11 | 2012-11-01 05:19:04.000 | 55555 | Account2
6679 | 11 | 2012-11-01 05:19:04.000 | -1 | NULL
6680 | 12 | 2012-11-02 05:20:04.000 | 12345 | Account1
6681 | 12 | 2012-11-02 05:20:04.000 | 55555 | Account2
6682 | 13 | 2012-11-04 06:20:04.000 | 44444 | Account3
6683 | 14 | 2012-11-05 05:30:04.000 | 44444 | Account3
6684 | 14 | 2012-11-05 05:31:04.000 | -1 | NULL
**CardVer_3**
BatchId |Name
11 |MasterCard
12 |Visa
13 |AMEX
14 |GoCard
This will be an intermediate table, the output is planned to look like the attached.
Gordon, I made some very minor changes to your suggestion and believe I have the correct output: http://www.sqlfiddle.com/#!3/cfbc3/7/0 . Thank you very much. I'm not at all familiar with the windows functions so I'm going to brush up on these.
The code is here:
select 'CashSale' as [Trans],
AccountId,
min(InsertDateTime),
(case when name LIKE '%VISA%' then 'VISA'
when name LIKE '%MasterCard%' then 'MasterCard'
when name LIKE '%AMEX%' then 'AMEX'
else 'Other'
end) as [CardType]
from (select AccountId, InsertDateTime, c.name,
row_number() over (partition by AccountId order by insertDateTime asc) as seqnum
from TransBatch b left join
CardVer_3 c WITH (NOLOCK) ON c.batchId = B.BatchId
) t
where seqnum = 1
group by t.accountid, t.name
The next steps are to dump this into a temp table and try and get the output looking like the attached excel screen.
It sounds like you are trying to get the full record for the minimum insert date time. For this, you want to use windows functions:
select 'CashSale' as Trans,
AccountId,
min(InsertDate),
(case when name LIKE '%VISA%' then 'VISA'
when name LIKE '%MasterCard%' then 'MasterCard'
when name LIKE '%AMEX%' then 'AMEX'
else 'Other'
end) as [CardType]
from (select AccountId, InsertDate, c.name,
row_number() over (partition by AccountId order by insertDate desc) as seqnum
from TransBatch b left join
CardVer_3 c WITH (NOLOCK)
ON c.Id = B.BatchId
) t
where seqnum = 1
I'm taking a guess that "CashSale" means that the credit card did not match. The TransId is then either the recordId or "CashSale".