Want SQL query for this scenario - sql

tbl_employee
empid empname openingbal
2 jhon 400
3 smith 500
tbl_transection1
tid empid amount creditdebit date
1 2 100 1 2016-01-06 00:00:00.000
2 2 200 1 2016-01-08 00:00:00.000
3 2 100 2 2016-01-11 00:00:00.000
4 2 700 1 2016-01-15 00:00:00.000
5 3 100 1 2016-02-03 00:00:00.000
6 3 200 2 2016-02-06 00:00:00.000
7 3 400 1 2016-02-07 00:00:00.000
tbl_transection2
tid empid amount creditdebit date
1 2 100 1 2016-01-07 00:00:00.000
2 2 200 1 2016-01-08 00:00:00.000
3 2 100 2 2016-01-09 00:00:00.000
4 2 700 1 2016-01-14 00:00:00.000
5 3 100 1 2016-02-04 00:00:00.000
6 3 200 2 2016-02-05 00:00:00.000
7 3 400 1 2016-02-08 00:00:00.000
Here 1 stand for credit and 2 for debit
I want output like
empid empname details debitamount creditamount balance Dr/Cr date
2 jhon opening Bal 400 Cr
2 jhon transection 1 100 500 Cr 2016-01-06 00:00:00.000
2 jhon transection 2 100 600 Cr 2016-01-07 00:00:00.000
2 jhon transection 1 200 800 Cr 2016-01-08 00:00:00.000
2 jhon transection 2 200 1000 Cr 2016-01-08 00:00:00.000
2 jhon transection 2 100 900 Dr 2016-01-09 00:00:00.000
2 jhon transection 1 100 800 Dr 2016-01-11 00:00:00.000
2 jhon transection 2 700 1500 Cr 2016-01-14 00:00:00.000
2 jhon transection 1 700 2200 Cr 2016-01-15 00:00:00.000
3 smith opening Bal 500 Cr
3 smith transection 1 100 600 Cr 2016-02-03 00:00:00.000
3 smith transection 2 100 700 Cr 2016-02-04 00:00:00.000
3 smith transection 2 200 500 Dr 2016-02-05 00:00:00.000
3 smith transection 1 200 300 Dr 2016-02-06 00:00:00.000
3 smith transection 1 400 700 Cr 2016-02-07 00:00:00.000
3 smith transection 2 400 1100 Cr 2016-02-08 00:00:00.000

You can do it with something like this:
select
empid, sum(amount) over (partition by empid order by date) as balance, details
from (
select
empid, case creditdebit when 1 then amount else -amount end as amount, date, details
from (
select empid, openingbal as amount, 1 as creditdebit, '19000101' as date, 'opening Bal' as details
from tbl_employee
union all
select empid, amount, creditdebit, date, 'transection 1'
from tbl_transection1
union all
select empid, amount, creditdebit, date, 'transection 2'
from tbl_transection2
) X
) Y
The innermost select is to gather the data from the 3 tables, the next one is to calculate +/- for the amounts and the outermost is to calculate the balance.
Example in SQL Fiddle

Related

standard sql: Get customer count and first purchase date per customer and store_id

I use standard sql and I need a query that gets the total count of purchases per customer, for each store_id. And also the first purchase date per customer, for each store_id.
I have a table with this structure:
customer_id
store_id
product_no
customer_no
purchase_date
price
1
10
100
200
2022-01-01
50
1
10
110
200
2022-01-02
70
1
20
120
200
2022-01-02
60
1
20
130
200
2022-01-02
40
1
30
140
200
2022-01-02
60
Current query:
Select
customer_id,
store_id,
product_id,
product_no,
customer_no,
purchase_date,
Price,
first_value(purchase_date) over (partition_by customer_no order by purchase_date) as first_purhcase_date,
count(customer_no) over (partition by customer_id, store_id, customer_no) as customer_purchase_count)
From my_table
This gives me this type of output:
customer_id
store_id
product_no
customer_no
purchase_date
price
first_purchase_date
customer_purchase_count
1
10
100
200
2022-01-01
50
2022-01-01
2
1
10
110
200
2022-01-02
70
2022-01-01
2
1
20
120
210
2022-01-02
60
2022-01-02
2
1
20
130
210
2022-01-02
40
2022-01-02
2
1
30
140
220
2022-01-10
60
2022-01-10
3
1
10
140
220
2022-01-10
60
2022-01-10
3
1
10
140
220
2022-01-10
60
2022-01-10
3
1
10
150
220
2022-01-10
60
2022-01-10
1
However, I want it to look like the table below in its final form. How can I achieve that? If possible I would also like to add 4 colums called "only_in_store_10","only_in_store_20","only_in_store_30","only_in_store_40" for all customer_no that only shopped at that store. It should mark with at ○ on each row of each customer_no that satisfies the condition.
customer_id
store_id
product_no
customer_no
purchase_date
price
first_purchase_date
customer_purchase_count
first_purchase_date_per_store
first_purchase_date_per_store
store_row_nr
1
10
100
200
2022-01-01
50
2022-01-01
2
2022-01-01
1
1
1
10
110
200
2022-01-02
70
2022-01-01
2
2022-01-02
1
1
1
20
120
210
2022-01-02
60
2022-01-02
2
2022-01-02
2
1
1
20
130
210
2022-01-03
40
2022-01-02
2
2022-01-02
2
1
1
30
140
220
2022-01-10
60
2022-01-10
3
2022-01-10
1
1
1
10
140
220
2022-01-11
50
2022-01-11
3
2022-01-11
2
1
1
10
140
220
2022-01-12
40
2022-01-11
3
2022-01-11
2
2
1
10
150
220
2022-01-13
60
2022-01-13
1
2022-01-13
1
1

Wrong results with group by for distinct count

I have these two queries for calculating a distinct count from a table for a particular date range. In my first query I group by location, aRID ( which is a rule ) and date. In my second query I don't group by a date.
I am expecting the same distinct count in both the results but I get total count as 6147 in first result and 6359 in second result. What is wrong here? The difference is group by..
select
r.loc
,cast(r.date as DATE) as dateCol
,count(distinct r.dC) as dC_count
from table r
where r.date between '01-01-2018' and '06-02-2018'
and r.loc = 1
group by r.loc, r.aRId, cast(r.date as DATE)
select
r.loc
,count(distinct r.DC) as dC_count
from table r
and r.date between '01-01-2018' and '06-02-2018'
and r.loc = 1
group by r.loc, r.aRId
loc dateCol dC_count
1 2018-01-22 1
1 2018-03-09 2
1 2018-01-28 3
1 2018-01-05 1
1 2018-05-28 143
1 2018-02-17 1
1 2018-05-08 187
1 2018-05-31 146
1 2018-01-02 3
1 2018-02-14 1
1 2018-05-11 273
1 2018-01-14 1
1 2018-03-18 2
1 2018-02-03 1
1 2018-05-20 200
1 2018-05-14 230
1 2018-01-11 5
1 2018-01-31 1
1 2018-05-17 209
1 2018-01-20 2
1 2018-03-01 1
1 2018-01-03 3
1 2018-05-06 253
1 2018-05-26 187
1 2018-03-24 1
1 2018-02-09 1
1 2018-03-04 1
1 2018-05-03 269
1 2018-05-23 187
1 2018-05-29 133
1 2018-03-21 1
1 2018-03-27 1
1 2018-05-15 202
1 2018-03-07 1
1 2018-06-01 155
1 2018-02-21 1
1 2018-01-26 2
1 2018-02-15 2
1 2018-05-12 331
1 2018-03-10 1
1 2018-01-09 3
1 2018-02-18 1
1 2018-03-13 2
1 2018-05-09 184
1 2018-01-12 2
1 2018-03-16 1
1 2018-05-18 198
1 2018-02-07 1
1 2018-02-01 1
1 2018-01-15 3
1 2018-02-24 4
1 2018-03-19 1
1 2018-05-21 161
1 2018-02-10 1
1 2018-05-04 250
1 2018-05-30 148
1 2018-05-24 153
1 2018-01-24 1
1 2018-05-10 199
1 2018-03-08 1
1 2018-01-21 1
1 2018-05-27 151
1 2018-01-04 3
1 2018-05-07 236
1 2018-03-25 1
1 2018-03-11 2
1 2018-01-10 1
1 2018-01-30 1
1 2018-03-14 1
1 2018-02-19 1
1 2018-05-16 192
1 2018-01-13 5
1 2018-01-07 1
1 2018-03-17 3
1 2018-01-27 2
1 2018-02-22 1
1 2018-05-13 200
1 2018-02-08 2
1 2018-01-16 2
1 2018-03-03 1
1 2018-05-02 217
1 2018-05-22 163
1 2018-03-20 1
1 2018-02-05 2
1 2018-02-11 1
1 2018-01-19 2
1 2018-02-28 1
1 2018-05-05 332
1 2018-05-25 211
1 2018-03-23 1
1 2018-05-19 219
loc dC_count
1 6359
From "COUNT (Transact-SQL)"
COUNT(DISTINCT expression) evaluates expression for each row in a group, and returns the number of unique, nonnull values.
The distinct is relative to the group, not to the whole table (or selected subset). I think this might be your misconception here.
To better understand what this means, take the following simplified example:
CREATE TABLE group_test
(a varchar(1),
b varchar(1),
c varchar(1));
INSERT INTO group_test
(a,
b,
c)
VALUES ('a',
'r',
'x'),
('a',
's',
'x'),
('b',
'r',
'x'),
('b',
's',
'y');
If we GROUP BY a and select count(DISTINCT c)
SELECT a,
count(DISTINCT c) #
FROM group_test
GROUP BY a;
we get
a | #
----|----
a | 1
b | 2
As there is only c='x' for a=1, there is only a distinct count of 1 for this group but 2 for the other group as it has 'x'and 'y' in c. The sum of counts is 3 here.
Now if we GROUP BY a, b
SELECT a,
b,
count(DISTINCT c) #
FROM group_test
GROUP BY a,
b;
we get
a | b | #
----|----|----
a | r | 1
a | s | 1
b | r | 1
b | s | 1
We get 1 for every count here as each value of c is the only one in the group. And all of a sudden the sum of counts is 4.
And if we get the distinct count of c for the whole table
SELECT count(DISTINCT c) #
FROM group_test;
we get
#
----
2
which sums up to 2.
The sum of the counts is different in each case but right none the less.
The more groups there are, the higher the chance for a value to be unique within that group. So your results seem totally plausible.
db<>fiddle

How to add status to the table

I have the following table where is clipping from my db. I have 2 types of contracts.
I: client pays for first 6mth 60$, next 6mth 120$ (111 client)
II: client pays for first 6mth 60$ but if want still pays 60$ the contract will be extended at 6mth, whole contract is 18mth. (321 client who still pays)
ID_Client | Amount | Amount_charge | Lenght | Date_from | Date_to | Reverse
--------------------------------------------------------------------------------
111 60 60 12 2015-01-01 2015-01-31 12
111 60 60 12 2015-02-01 2015-02-28 11
111 60 60 12 2015-03-01 2015-03-31 10
111 60 60 12 2015-04-01 2015-04-30 9
111 60 60 12 2015-05-01 2015-05-31 8
111 60 60 12 2015-06-01 2015-06-30 7
111 120 60 12 2015-07-01 2015-07-31 6
111 120 60 12 2015-08-01 2015-08-31 5
111 120 60 12 2015-09-01 2015-09-30 4
111 120 60 12 2015-10-01 2015-10-31 3
111 120 60 12 2015-11-01 2015-11-30 2
111 120 60 12 2015-12-01 2015-12-31 1
111 120 60 12 2016-01-01 2015-01-31 0
111 120 60 12 2016-02-01 2015-02-29 0
321 60 60 12 2015-01-01 2015-01-31 12
321 60 60 12 2015-02-01 2015-02-28 11
321 60 60 12 2015-03-01 2015-03-31 10
321 60 60 12 2015-04-01 2015-04-30 9
321 60 60 12 2015-05-01 2015-05-31 8
321 60 60 12 2015-06-01 2015-06-30 7
321 60 60 12 2015-07-01 2015-07-31 6
321 60 60 12 2015-08-01 2015-08-31 5
321 60 60 12 2015-09-01 2015-09-30 4
321 60 60 12 2015-10-01 2015-10-31 3
321 60 60 12 2015-11-01 2015-11-30 2
321 60 60 12 2015-12-01 2015-12-31 1
321 60 60 12 2016-01-01 2016-01-30 0
321 60 60 12 2016-02-01 2016-02-31 0
321 60 60 12 2016-03-01 2016-03-30 0
321 60 60 12 2016-04-01 2016-04-31 0
I need to add status column.
A - normal period of agreement
D - where the agreement is doubled after 6mth but after 12mth is E(nd of agreemnt)
E - where contract is finished
L - where contract after 6mth was extended, after 18mth the status will be type E
For 321 Client after 12mth the lenght of contract was updated from 12 to 18
I have a lot of clients so i think better will be using loop to go by all clients?
ID_Client | Amount | Amount_charge | Lenght | Date_from | Date_to | Reverse | Status
-----------------------------------------------------------------------------------------
111 60 60 12 2015-01-01 2015-01-31 12 A
111 60 60 12 2015-02-01 2015-02-28 11 A
111 60 60 12 2015-03-01 2015-03-31 10 A
111 60 60 12 2015-04-01 2015-04-30 9 A
111 60 60 12 2015-05-01 2015-05-31 8 A
111 60 60 12 2015-06-01 2015-06-30 7 A
111 120 60 12 2015-07-01 2015-07-31 6 D
111 120 60 12 2015-08-01 2015-08-31 5 D
111 120 60 12 2015-09-01 2015-09-30 4 D
111 120 60 12 2015-10-01 2015-10-31 3 D
111 120 60 12 2015-11-01 2015-11-30 2 D
111 120 60 12 2015-12-01 2015-12-31 1 D
111 120 60 12 2016-01-01 2015-01-31 0 E
111 120 60 12 2016-02-01 2015-02-29 0 E
321 60 60 12 2015-01-01 2015-01-31 12 A
321 60 60 12 2015-02-01 2015-02-28 11 A
321 60 60 12 2015-03-01 2015-03-31 10 A
321 60 60 12 2015-04-01 2015-04-30 9 A
321 60 60 12 2015-05-01 2015-05-31 8 A
321 60 60 12 2015-06-01 2015-06-30 7 A
321 60 60 12 2015-07-01 2015-07-31 6 L
321 60 60 12 2015-08-01 2015-08-31 5 L
321 60 60 12 2015-09-01 2015-09-30 4 L
321 60 60 12 2015-10-01 2015-10-31 3 L
321 60 60 12 2015-11-01 2015-11-30 2 L
321 60 60 12 2015-12-01 2015-12-31 1 L
321 60 60 18 2016-01-01 2016-01-30 0 L
321 60 60 18 2016-02-01 2016-02-31 0 L
321 60 60 18 2016-03-01 2016-03-30 0 L
321 60 60 18 2016-04-01 2016-04-31 0 L
If the Reverse column is what I think:
update table1 a
set "Status"=
CASE
WHEN A."Reverse" > 6 THEN
'A'
WHEN A."Reverse" > 0 THEN
DECODE (A."Amount", A."Amount_charge", 'L', 'D')
ELSE
CASE
WHEN A."Amount" <> A."Amount_charge" THEN
'E'
ELSE
CASE WHEN ADD_MONTHS ( (SELECT b."Date_from" FROM table1 b WHERE a."ID_Client" = b."ID_Client" AND b."Reverse" = 1),6) > a."Date_from" THEN 'L'
ELSE
'E'
END
END
END
Better is to calculate the sums. The amount per month come from first payment. Something like this:
DECLARE
CURSOR c2
IS
SELECT ID_CLIENT, --AMOUNT, AMOUNT_CHARGE, LENGTH, DATE_FROM, DATE_TO, REVERSE, STATUS,
FIRST_VALUE (amount_charge) OVER (PARTITION BY id_client ORDER BY date_from) first_amount_charge,
SUM (amount) OVER (PARTITION BY id_client ORDER BY date_from) sum_amount,
SUM (amount_charge) OVER (PARTITION BY id_client ORDER BY date_from) sum_amount_charge
FROM TABLE2
FOR UPDATE NOWAIT;
BEGIN
FOR c1 IN c2
LOOP
UPDATE table2
SET status = CASE WHEN c1.sum_amount <= 6 * c1.first_amount_charge THEN 'A'
WHEN c1.sum_amount > 18 * c1.first_amount_charge THEN 'E'
WHEN c1.sum_amount > c1.sum_amount_charge THEN 'D'
ELSE 'L'
END
WHERE CURRENT OF c2;
END LOOP;
END;

SQL - Compare rows by id, date and amount

I need to SELECT a row in which issue_date = maturity_date of another row with same id, and same amount_usd.
I tried with self join, but I do not get right result.
Here is a simplified version of my table:
ID ISSUE_DATE MATURITY_DATE AMOUNT_USD
1 2010-01-01 00:00:00.000 2015-12-01 00:00:00.000 5000
1 2010-01-01 00:00:00.000 2001-09-19 00:00:00.000 700
2 2014-04-09 00:00:00.000 2019-04-09 00:00:00.000 400
1 2015-12-01 00:00:00.000 2016-12-31 00:00:00.000 5000
5 2015-02-24 00:00:00.000 2015-02-24 00:00:00.000 8000
4 2012-11-29 00:00:00.000 2015-11-29 00:00:00.000 10000
3 2015-01-21 00:00:00.000 2018-01-21 00:00:00.000 17500
2 2015-02-02 00:00:00.000 2015-12-05 00:00:00.000 12000
1 2015-01-12 00:00:00.000 2018-01-12 00:00:00.000 18000
2 2015-12-05 00:00:00.000 2016-01-10 00:00:00.000 12000
Result should be:
ID ISSUE_DATE MATURITY_DATE AMOUNT_USD
1 2015-12-01 00:00:00.000 2016-12-31 00:00:00.000 5000
2 2015-12-05 00:00:00.000 2016-01-10 00:00:00.000 12000
Thanks in advance!
Do following: http://sqlfiddle.com/#!6/c0a02/1
select a.id, a.issue_date, a.maturity_date, a.amount_usd
from tbl a
inner join tbl b
on a.id = b.id
and a.maturity_date = b.issue_date
-- added to prevent same maturity date and issue date
where a.maturity_date <> a.issue_date
Output:
| id | issue_date | maturity_date | amount_usd |
|----|----------------------------|----------------------------|------------|
| 1 | January, 01 2010 00:00:00 | December, 01 2015 00:00:00 | 5000 |
| 2 | February, 02 2015 00:00:00 | December, 05 2015 00:00:00 | 12000 |

SQL- calculating daily stock levels for month as aggregate of availability

I have a table containing following records of stocks from different depots in region. this contains:
itemName
startDate
endDate
quantity
The fields are
key(pk)
itemName- numeric code
startDate- date
endDate- date
amt- number
Sample data with 3 item types
1 101 Jan 1, 2013 Jan 14, 2013 15
2 101 Jan 12, 2013 Jan 15, 2013 3
3 102 Jan 4, 2013 Jan 26, 2013 7
4 102 Jan 6, 2013 Jan 12, 2013 19
5 103 Jan 15, 2013 Jan 16, 2013 3
6 103 Jan 12, 2013 Jan 21, 2013 19
How do I write a query that will get the number of items of each time every day in this period? Essentially I need to have a query that will add up applicable items between startDate and endDate. Thanks
I would want a final query result to look like that would add overlaps for each item
Jan 1 101 15
Jan 1 102 0
Jan 12 101 18
Jan 15 101 3
Jan 16 101 3
while I know i can do for a given date
SELECT item, sum(amt)
FROM [table]
WHERE (date>=startdate) AND (date<=enddate)
GROUP BY item
How do I enable it iterate for the whole month(Jan 1st to 31st) to produce such a report?
Here's what you need to do:
Create a table named [DayNumbers] and fill it with the numbers from 1 through 31:
DayNumber
---------
1
2
3
...
30
31
Now create a saved query in Access named [MonthDates] to create a row for each day in a specified month:
PARAMETERS SelectedYear Long, SelectedMonth Long;
SELECT DateSerial([SelectedYear], [SelectedMonth], DayNumber) AS StatusDate
FROM DayNumbers
WHERE Month(DateSerial([SelectedYear], [SelectedMonth], DayNumber)) = [SelectedMonth];
Note that the WHERE clause restricts the number of days to the actual number of days in the month (e.g., 30 for April).
Create another saved query in Access named [StockStatusRows] to create a row for each day and each item
SELECT StatusDate, itemName
FROM
MonthDates,
(
SELECT DISTINCT itemName FROM StockData
) AS Items;
For test data in [StockStatus] that looks like
key itemName startDate endDate amt
--- -------- ---------- ---------- ---
1 101 2013-01-01 2013-01-14 15
2 101 2013-01-12 2013-01-15 3
3 102 2013-01-04 2013-01-26 7
4 102 2013-01-06 2013-01-12 19
5 103 2013-01-15 2013-01-16 3
6 103 2013-01-12 2013-01-21 19
7 101 2013-01-30 2013-02-03 6
8 102 2013-02-05 2013-02-23 9
9 103 2013-02-07 2013-03-02 11
the [StockStatusRows] query will return
StatusDate itemName
---------- --------
2013-01-01 101
2013-01-02 101
2013-01-03 101
..
2013-01-30 101
2013-01-31 101
2013-01-01 102
2013-01-02 102
2013-01-03 102
...
2013-01-30 102
2013-01-31 102
2013-01-01 103
2013-01-02 103
2013-01-03 103
...
2013-01-30 103
2013-01-31 103
Now we can pull together the actual stock values like so:
SELECT ssr.StatusDate, ssr.itemName, Nz(sums.total, 0) AS TotalOnHand
FROM
StockStatusRows AS ssr
LEFT JOIN
(
SELECT StatusDate, itemName, Sum(amt) AS total
FROM
(
SELECT md.StatusDate, sd.itemName, sd.amt
FROM
StockData sd
INNER JOIN
MonthDates md
ON md.StatusDate>=sd.startDate
And md.StatusDate<=sd.endDate
)
GROUP BY StatusDate, itemName
) AS sums
ON (sums.itemName=ssr.itemName)
AND (sums.StatusDate=ssr.StatusDate)
ORDER BY ssr.StatusDate, ssr.itemName;
returning
StatusDate itemName TotalOnHand
---------- -------- -----------
2013-01-01 101 15
2013-01-01 102 0
2013-01-01 103 0
2013-01-02 101 15
2013-01-02 102 0
2013-01-02 103 0
2013-01-03 101 15
2013-01-03 102 0
2013-01-03 103 0
2013-01-04 101 15
2013-01-04 102 7
2013-01-04 103 0
2013-01-05 101 15
2013-01-05 102 7
2013-01-05 103 0
2013-01-06 101 15
2013-01-06 102 26
2013-01-06 103 0
...
2013-01-12 101 18
2013-01-12 102 26
2013-01-12 103 19
2013-01-13 101 18
2013-01-13 102 7
2013-01-13 103 19
2013-01-14 101 18
2013-01-14 102 7
2013-01-14 103 19
2013-01-15 101 3
2013-01-15 102 7
2013-01-15 103 22
2013-01-16 101 0
2013-01-16 102 7
2013-01-16 103 22
2013-01-17 101 0
2013-01-17 102 7
2013-01-17 103 19
...
2013-01-22 101 0
2013-01-22 102 7
2013-01-22 103 0
...
2013-01-31 101 6
2013-01-31 102 0
2013-01-31 103 0
select itemName from <Table-Name> where startDate>=(start-date) startDate<=(end-date) and endDate>=(start-date) and endDate<=(end-date) group by itemName.
This would calculate the sum of quantities of each product between (start-date) and (end-date).
If you want just total of all items irrespective of their type,
select sum(quantity) from <Table-Name> where startDate>=(start-date) startDate<=(end-date) and endDate>=(start-date) and endDate<=(end-date)
Hope this helps