Insert specific row of a table into same table ONCE only - sql

I have a table containing the below elements:
YEAR | MONTH | COMP | COMP_DESC | FIRST_NAME | LAST_NAME | EMP_SD | CURR | METHOD | POSITION_STATUS
2021 | 2 | ABC | ABC Company | Jake | Sam | 11-01-2021 | USD | |
2021 | 5 | XYZ | XYZ Company | Neo | June | 23-09-2021 | USD | OPEN | METH_004
The Result i need:
YEAR | MONTH | COMP | COMP_DESC | FIRST_NAME | LAST_NAME | EMP_SD | CURR | METHOD | POSITION_STATUS
2021 | 2 | ABC | ABC Company | Jake | Sam | 11-01-2021 | USD | |
2021 | 5 | XYZ | XYZ Company | Neo | June | 23-09-2021 | USD | OPEN | METH_004
2021 | 5 | XYZ | XYZ Company | RP_Neo | RP_June | 24-09-2021 | USD | |
When POSITION_STATUS='METH_004' AND METHOD='OPEN', I need the specific row to be duplicated in the same table with change in date and change in name JUST ONCE. and when i re run the query i dont want any more duplication happening
Below is the Query i have written:
INSERT INTO Table1
(YEAR, MONTH, COMP, COMP_DESC, FIRST_NAME, LAST_NAME, EMP_SD, CURR)
SELECT T.YEAR, T.MONTH, T.COMP, T.COMP_DESC, CONCAT('RP_',FIRST_NAME) AS N1, CONCAT('RP_',LAST_NAME) AS N2,
EMP_DATE + INTERVAL '1 day' AS NXT_DAY, T.CURR FROM Table1 T WHERE POSITION_STATUS='OPEN' AND METHOD = 'METH_004'
EXCEPT
SELECT T1.POS, T1.YEAR, T1.MONTH, T1.COMP, T1.COMP_DESC, T1.COST_CENTER
FROM Table1 T1
INNER JOIN Table T2
ON T1.YEAR=T2.YEAR
AND T1.MONTH=T2.MONTH
AND T1.COMP=T2.COMP
AND T1.COMP_DESC=T2.COMP_DESC
AND T1.CURR=T2.CURR;
Can anyone suggest me changes to the code to get the result. The above code runs without error. But the row is not replicated.
Thanks

Related

How to split single row yearly values into multi row monthly values in PostgreSQL?

I have a table of values from yearly payments like this:
| id | date | yearly_payment |
| ------ | -------- | :--------------: |
| 1 | 06/01/21 | $600 |
| 2 | 06/01/22 | $720 |
What I am trying to achieve is:
| id | date | monthly_payment |
| ------ | -------- | :---------------: |
| 1 | 06/01/21 | $50 |
| 1 | 07/01/21 | $50 |
| 1 | ... | $50 |
| 1 | 05/01/21 | $50 |
| 2 | 06/01/22 | $60 |
| 2 | 07/01/22 | $60 |
| 2 | ... | $60 |
| 2 | 05/01/22 | $60 |
I thought I could achieve this through some transformation on a pivot table, but to no avail. This solution gets me close, but I can't quite figure out how to achieve it within Postgres.
Would this work?
select
y.id, y.date + interval '1 month' * gs.a as date,
y.yearly_payment / 12 as monthly_payment
from
yearly_payments y
cross join generate_series (0, 11) gs (a)
Beware of rounding... if yearly_payment is an integer, then you would want to divide by 12.0 to force a numeric context.

SQL some selections into one (or get two colums from one)

I use PostgreSql, I have two tables (for example)
Let table1 will contain stores, there are 2 types 'candy store' and 'dental store'.
Each row contains information about a customer's purchase in a particular store
In result i want to get money from each type of store group by id and the last date of purchase. Money from candy stores start sum since 2016, but money from dental stores start sum from 2018
table1:
+----+---------+------------------+-------+
| id | store | date of purchase | money |
| 1 | store 1 | 2016-01-01 | 10 |
| 1 | store 5 | 2018-01-01 | 50 |
| 2 | store 2 | 2017-01-20 | 10 |
| 2 | store 3 | 2019-02-20 | 15 |
| 3 | store 2 | 2017-02-02 | 20 |
| 3 | store 6 | 2019-01-01 | 60 |
| 1 | store 1 | 2015-01-01 | 20 |
+----+---------+------------------+-------+
table2 :
+---------+--------+
| store | type |
| store 1 | candy |
| store 2 | candy |
| store 3 | candy |
| store 4 | dental |
| store 5 | dental |
| store 6 | dental |
+---------+--------+
I want my query to return a table like this:
+----+---------------+-----------------+---------------+-----------------+
| id | money( candy) | the last date c | money(dental) | the last date d |
| 1 | 10 | 2016-01-01 | 50 | 2018-01-01 |
| 2 | 25 | 2019-02-20 | - | - |
| 3 | 20 | 2017-02-02 | 60 | 2019-01-01 |
+----+---------------+-----------------+---------------+-----------------+
if I understand correctly , this is what you want to do :
select id
, sum(money) filter (where ty.type = 'candy') candymoney
, max(purchasedate) filter (where ty.type = 'candy') candylastdate
, sum(money) filter (where ty.type = 'dental') dentalmoney
, max(purchasedate) filter (where ty.type = 'dental') dentallastdate
from table t
join storetype table st on t.store = ty.store
group by id

Get rowset based on distinct combination of columns

Given this dataset, each stock has a yearly snapshot of value.
+----+------+------+-------+-------+
| ID | Name | Year | Stock | Value |
+----+------+------+-------+-------+
| 1 | John | 2019 | ABC | 123 |
| 1 | John | 2020 | ABC | 123 |
| 1 | John | 2021 | ABC | 123 |
| 1 | John | 2021 | XYZ | 200 |
| 1 | John | 2022 | ABC | 123 |
| 1 | John | 2022 | XYZ | 200 |
| 1 | John | 2023 | ABC | 630 |
| 1 | John | 2023 | XYZ | 200 |
+----+------+------+-------+-------+
In 2019, John only holds ABC with a value of 123
In 2020, John also only holds ABC, with a value of 123 (has not changed)
In 2021, John holds ABC but has also acquired XYZ, with a value of 200
in 2022, John holds ABC and XYZ, both of which values haven't changed.
In 2023, John holds ABC and XYZ, with ABC's value increasing to 630 and XYZ's value remaining at 200.
I would like to return rows so that
Per year, if nothing of John's portfolio has changed SINCE THE LAST YEAR, no rows are returned
If anything in John's portfolio has changed SINCE THE LAST YEAR, all his current holdings are listed
For example,
+----+------+------+-------+-------+
| ID | Name | Year | Stock | Value |
+----+------+------+-------+-------+
| 1 | John | 2019 | ABC | 123 |
| 1 | John | 2021 | ABC | 123 |
| 1 | John | 2021 | XYZ | 200 |
| 1 | John | 2023 | ABC | 630 |
| 1 | John | 2023 | XYZ | 200 |
+----+------+------+-------+-------+
How would I do this, whether it be through functions in PL/SQL or in pure SQL?
If there are not too many rows per user, then listagg() provides a convenient solution:
select ny.*
from (select name, year,
listagg(stock || ':' || value, ',') within group (order by stock) as stocks,
lag(listagg(stock || ':' || value, ',') within group (order by stock)) as prev_stocks,
lag(year) over (partition by name order by year) as prev_year
from t
group by name, year
) ny
where prev_year is null or prev_year <> year - 1 or prev_stocks <> stocks;
Alternatively, you can check each row individually and use an analytic function to project the information over all rows in a name/year:
select t.*
from (select t.*,
sum(case when prev_nsv_year = year then 0 else 1 end) over (partition by name, year) as num_diff,
lag(cnt) over (partition by name order by year) as prev_cnt
from (select t.*,
lag(year) over (partition by name, stock, value over order by year) as prev_nsv_year,
count(*) over (partition by name, year) as cnt
from t
) t
) t
where cnt <> prev_cnt or prev_cnt is null or
num_diff > 0;

Having Groups based on distinct count of another column

I have a table as follow :
+-------------+-----------+------+
| GroupNumber | TeamName | Goal |
+-------------+-----------+------+
| 1 | Sales | ABC |
| 1 | Sales | ABC |
| 1 | Sales | ABC |
| 1 | Design | XYZ |
| 2 | Design | XYZ |
| 2 | Sales | XYZ |
| 2 | technical | XYZ |
| 2 | Support | XYZ |
| 3 | Sales | XYZ |
| 3 | Sales | XYZ |
| 3 | Sales | XYZ |
+-------------+-----------+------+
I want to output only the groups that have unique teams greater than 3.
Only group 2 has this condition so the output is :
Expected Output:
+-------------+-----------+------+
| GroupNumber | TeamName | Goal |
+-------------+-----------+------+
| 2 | Design | XYZ |
| 2 | Sales | XYZ |
| 2 | technical | XYZ |
| 2 | Support | XYZ |
+-------------+-----------+------+
not sure how to utilize this in subquery
SELECT count(Distinct(TeamName))
FROM mytable
group by [GroupNumber]
HAVING COUNT(Distinct[TeamName])>3
Simply put it in a Subquery:
select *
from mytable
where [GroupNumber] in
(
SELECT [GroupNumber]
FROM mytable
group by [GroupNumber]
HAVING COUNT(Distinct[TeamName])>3
)
Please try
SELECT *
FROM mytable where GroupNumber in (select GroupNumber
FROM mytable group by TeamName
HAVING COUNT(TeamName)>3)

SQL Join Help - Sum and Apply Filter to Tables Before Join

I have a Stock View (that lists all the individual pieces of stock and the stock date) and a Sales View (that lists all of the sales and the date the sale occurred).
Stock View:
+----+------+-----+------------+
| ID | Item | Qty | Date |
+----+------+-----+------------+
| 1 | A | 3 | 01/01/2000 |
| 2 | A | 2 | 02/02/2000 |
| 3 | D | 9 | 05/06/2000 |
| 4 | F | 22 | 09/01/2001 |
| 5 | A | 10 | 01/04/2001 |
| 6 | C | 12 | 01/01/2002 |
+----+------+-----+------------+
Sales View:
+------+-----+------------+
| Item | Qty | Date |
+------+-----+------------+
| B | 3 | 01/01/2001 |
| B | 77 | 01/12/2001 |
| C | 9 | 02/02/2002 |
| A | 10 | 03/03/2002 |
| G | 2 | 05/06/2002 |
| C | 3 | 09/10/2012 |
+------+-----+------------+
I want to join these tables..but before doing so:
Stock view needs to be filtered between 2 date parameters #StockFrom and #StockTo
Sales view needs to be filtered between 2 date parameters #SalesFrom and #SalesTo
Sales view then needs to be grouped by Item and have the Qty Summed (so the date field needs to be dropped although it is being filtered on) and then joined onto the Stock View on the Item field.
So in essence I want to see the Stock View as it is (but filtered on dates) with an extra column showing the sales that have occurred between 2 dates for that item.
Desired Output:
+----+------+-----+------------+-------+
| ID | Item | Qty | Date | Sales |
+----+------+-----+------------+-------+
| 1 | A | 3 | 01/01/2000 | 10 |
| 2 | A | 2 | 02/02/2000 | 10 |
| 3 | D | 9 | 05/06/2000 | 0 |
| 4 | F | 22 | 09/01/2001 | 0 |
| 5 | A | 10 | 01/04/2001 | 10 |
| 6 | C | 12 | 01/01/2002 | 12 |
+----+------+-----+------------+-------+
Thanks to any help in advance!
SELECT
Stock.*,
IFNULL(SUM(Sales.Qty),0) AS Sales
FROM Stock
LEFT JOIN Sales ON Stock.Item=Sales.Item
WHERE Stock.Date BETWEEN #StockFrom AND #StockTo
AND (
Sales.Date BETWEEN #SalesFrom AND #SalesTo
OR Sales.Date IS NULL
)
GROUP BY Stock.ID
This is for MySQL, as you didn't specify the diaclect. SQLfiddle
EDIT
SELECT
Stock.ID AS ID,
MIN(Stock.Item) AS Item,
MIN(Stock.Qty) AS Qty,
MIN(Stock.Date) AS Date,
CASE WHEN SUM(Sales.Qty) IS NULL THEN 0 ELSE SUM(Sales.Qty) END AS Sales
FROM Stock
LEFT JOIN Sales ON Stock.Item=Sales.Item
WHERE Stock.Date BETWEEN #StockFrom AND #StockTo
AND (
Sales.Date BETWEEN #SalesFrom AND #SalesTo
OR Sales.Date IS NULL
)
GROUP BY Stock.ID
works for MS SQL (SQLfiddle)
Please try below query for MS Sql Server:
SELECT DISTINCT
a.ID,
a.Item,
a.Qty,
a.Date,
ISNULL(SUM(b.Qty) OVER (PARTITION BY a.Item, a.[Date]), 0) Sales
FROM
StockView a LEFT JOIN SalesView b on a.Item=b.Item