SQL Procedure (Sybase Advantage Database Server) - sql

I have table contain below data:
EMPCODE PAYCODE AMOUNT
------------------------
001 A 100
001 B 200
002 A 120
002 C 80
003 B 50
003 D 20
All PAYCODE in table at the moment are A, B, C, D.
However, other EMPCODE with other new PAYCODE such as E or F, might be added in later on.
EMPCODE 001 has PAYCODE A and B (he doesn't have PAYCODE C and D).
EMPCODE 002 has PAYCODE A and C (he doesn't have PAYCODE B and D).
EMPCODE 003 has PAYCODE B and D (he doesn't have PAYCODE A and C).
I want to create a simple stored procedure / SQL which can add the dummy records for each EMPCODE for PAYCODE which they don't own.
My expected result as below:
EMPCODE PAYCODE AMOUNT
------------------------
001 A 100
001 B 200
001 C 0
001 D 0
002 A 120
002 B 0
002 C 80
002 D 0
003 A 0
003 B 50
003 C 0
003 D 20
I can achieve that through coding but I need to do it via a stored procedure.
Is there any SQL stored procedure to achieve this?
Appreciate for the answer.

Use Join to get the result. SQLFiddle
SELECT C.EMPCODE, C.PAYCODE, ISNULL(D.AMOUNT, 0) AS AMOUNT FROM
(
SELECT * FROM
(SELECT EMPcode from Test GROUP BY EMPCODE) AS A,
(SELECT Paycode FROM Test GROUP BY PAYCODE) AS B
) AS C
LEFT JOIN Test AS D
ON C.EMPCODE=D.EMPCODE AND C.PAYCODE = D.PAYCODE
UPDATE:
1) To get the distinct EMPCODE from table
(SELECT EMPcode from Test GROUP BY EMPCODE) AS A
2) To get the distinct PAYCODE from table
(SELECT Paycode FROM Test GROUP BY PAYCODE) AS B
3) To get the all PAYCODE value for each Empcode.
SELECT * FROM
(SELECT EMPcode from Test GROUP BY EMPCODE) AS A,
(SELECT Paycode FROM Test GROUP BY PAYCODE) AS B

You can do this by generating all the combinations of empcode and paycode using a cross join in a sub-query that you then use as a derived table for a left join. To not insert already existing rows you should exclude them using a correlated not exists predicate. Written as a stored procedure it could look like this:
create proc insert_missing_values as
insert your_table (empcode, paycode, amount)
select distinct codes.empcode, codes.paycode, isnull(your_table.amount, 0) amount
from (
select t1.empcode, t2.paycode
from your_table t1, your_table t2
group by t1.empcode, t2.paycode
) codes
left join your_table on
codes.empcode = your_table.empcode
and
codes.paycode = your_table.paycode
where not exists (
select 1 from your_table
where codes.empcode = your_table.empcode and codes.paycode = your_table.paycode
)
Sample SQL Fiddle
Edit: as Sybase ASE doesn't support the explicit cross join you can use a unqualified implicit join with the same effect by doing from TableA, TableB which returns the Cartesian product of rows. See this Wikipedia article for an explanation.

Related

SQL select units with condition of another column

I have this
ID
Product
001
A
001
B
001
C
002
A
002
A
002
D
003
G
003
D
003
C
004
G
004
D
004
R
and I wand ID list if they don't have product C...so:
002
You can apply the set difference between all ids and all ids with a "c" with the standard way of doing it: using the NOT IN operator in the WHERE clause.
SELECT DISTINCT ID
FROM tab
WHERE id NOT IN (SELECT ID FROM tab WHERE Product = 'C')
Consider below query:
SELECT DISTINCT ID FROM (
SELECT *, COUNTIF(product = 'C') OVER (PARTITION BY ID) AS cnt_C
FROM sample_table
) WHERE cnt_C = 0
output:
There are multiple ways of doing this, but I think a very readable way is to use NOT EXISTS.
SELECT DISTINCT id
FROM mytable t1
WHERE NOT EXISTS (SELECT 1 FROM mytable t2 WHERE t2.id = t1.id AND t2.Product = 'C')
The WHERE clause checks that there is no row with product C and the same id. The DISTINCT ensures you don't get multiples of the same id returned.
I am not sure but I think you just need a normal where
SELECT * FROM table_name WHERE product != product_name
*insert the table name in the place of Table_name and product name in the place of product_name
select distinct
id
from
your_table_name
where
product <> 'C';
This will return you list of ID's that don't have product C

SQL - Find number of new starters and leavers between snapshot dates

I have an SQL table staff that takes a snapshot on specific dates and adds a row of every staffID and the corresponding DateID.
I need to find out how many staff have joined since the next DateID, and how many have left.
So in the example staff table below at DateID B, StaffID's 002 and 003 from DateID A aren't there so have 'left', and DateID B has staffID's 004,005,006 that were not there in DateID so are 'new'.
StaffID DateID
007 C
005 C
006 B
005 B
004 B
001 B
003 A
002 A
001 A
I've surmised how these results would appear in the below.
DateID New Leavers
A 0 2
B 3 2
C 1 3
My current and only way of solving this is to go through each DateID with the DateID before it and left join the older date counting the rows where the old date it null for the number of new staff and swapping the tables round for the leavers.
SELECT t1.DateID, count(*) AS Total
(SELECT *
FROM staff
WHERE DateID = 'B') t1
LEFT JOIN
(SELECT *
FROM staff
WHERE DateID = 'A') t2
ON t1.StaffID = t2.StaffID
WHERE t2.StaffID is null
GROUP BY t1.DateID
This method is horribly inefficient with a larger table and hoping anyone has ideas for a way to do in one script. Alternatively, a script just for new staff and a script for just leavers would be as good.
As requested by #Larnu, I've added a snapshot table that holds all the DateID's. the staff table is filtered to just show DateID's that are weekly.
DateID Weekly Monthly Yearly
A Y Y N
B Y N N
C Y N N
D N N N
E Y Y N
F N N Y
LEAD and LAG window functions would help here.
Since the DateIDs are not consecutive, you need to calculate LEAD/LAG on that also, and join it
SELECT
s.DateID,
[New] = COUNT(CASE WHEN s.PrevID IS NULL OR s.PrevID <> d.PrevDateID THEN 1 END),
Leavers = COUNT(CASE WHEN s.NextID IS NULL OR s.NextID <> d.NextDateID THEN 1 END)
FROM (
SELECT *,
PrevDateID = LAG(DateID) OVER (ORDER BY DateID),
NextDateID = LEAD(DateID) OVER (ORDER BY DateID)
FROM Dates d
) d
JOIN (
SELECT *,
PrevID = LAG(s.DateID) OVER (PARTITION BY StaffID ORDER BY DateID),
NextID = LEAD(s.DateID) OVER (PARTITION BY StaffID ORDER BY DateID)
FROM staff s
) s ON s.DateID = d.DateID
GROUP BY
s.DateID;

How to identify non-existing keys with reference to a table that has all mandatory keys, SQL?

I have the table 'Table01' which contains the keys that should be mandatory:
id
1
2
3
4
And I also have the table 'Table02' which contains the records to be filtered:
id
customer
weight
1
a
100
2
a
300
3
a
200
4
a
45
1
b
20
2
b
100
3
b
17
1
c
80
4
c
90
2
d
30
3
d
30
4
d
50
So I want to identify which are the mandatory id's that the table 'Table02' does not have, and in turn identify which is the 'customer' of each id's that the table 'Table02' does not have.
The resulting table should look like this:
customer
id
b
4
c
2
c
3
d
1
What I have tried so far is a 'rigth join'.
proc sql;
create table table03 as
select
b.id
from table02 a
right join table01 b
on a.id=b.id
where a.id is null;
run;
But that query is not identifying all the id's that should be mandatory.
I hope someone can help me, thank you very much.
here is one way:
select cl.customerid , a.id
from
Table1 a
cross join
( select customerid
from table2
group by customerid
) cl
where not exists ( select 1 from table2 b
where b.customerid = cl.customerid
and b.id = a.id
)
You can use an EXCEPT between two sub-selects. The first creates a matrix of all possibilities, and the except table is a selection of the extant customers.
Example:
data ids;
do id = 1 to 4; output; end;
run;
data have;
input id customer $ weight;
datalines;
1 a 100
2 a 300
3 a 200
4 a 45
1 b 20
2 b 100
3 b 17
1 c 80
4 c 90
2 d 30
3 d 30
4 d 50
run;
proc sql;
create table want(label='Customers missing some ids') as
select matrix.*
from
(select distinct have.customer, ids.id from have, ids) as matrix
except
(select customer, id from have)
;
quit;
If you are doing it in SQL server. Something like #eshirvana above posted, but also you can use with cte:
;with cte as
(
SELECT t1.id, t2.Customer
FROM Table01 t1
cross join (select distinct customer from Table02)
)
SELECT a.customer, a.id FROM cte a
LEFT JOIN Table02 b
ON a.id=b.id AND a.customer=b.customer
where b.id is null

How to join only latest date values from another table and prevent duplication

I'm trying to lookup a unique value from table b and get it into table a.
Table b stores multiple values that are changing by date.
I would like to join but only getting the values with the latest date from table b.
Table a
Unique ID
1
2
Table b
Date Unique ID Price
01/01/2019 1 100
01/02/2019 1 101
01/03/2019 1 102
01/01/2019 2 90
01/02/2019 2 91
01/03/2019 2 92
Expected result
Unique ID Price Date
1 102 01/03/2019
2 92 01/03/2019
Appreciate your help!
Have a sub-query that returns each UniqueID together with its max date. IN that result.
select * from tablename
where (UniqueID, date) in (select UniqueID, max(date)
from tablename
group by UniqueID)
You want correlated subquery :
select b.*
from tableb b
where b.date = (select max(b1.date) from tableb b1 where b1.UniqueID = b.UniqueID);
If you want to go with JOIN then you can do JOIN with subquery :
select a.UniqueID , b.Price, b.Date
from tablea a inner join
tableb b
on b.UniqueID = a.UniqueID
where b.date = (select max(b1.date) from tableb b1 where b1.UniqueID = a.UniqueID);
A correlated subquery?
select b.*
from b
where b.date = (select max(b2.date) from b b2 where b2.unique_id = b.unique_id);

Get the max value of a column from set of rows

I have a table like this
Table A:
Id Count
1 4
1 16
1 8
2 10
2 15
3 18
etc
Table B:
1 sample1.file
2 sample2.file
3 sample3.file
TABLE C:
Count fileNumber
16 1234
4 2345
15 3456
18 4567
and so on...
What I want is this
1 sample1.file 1234
2 sample2.file 3456
3 sample3.file 4567
To get the max value from table A I used
Select MAX (Count) from A where Id='1'
This works well but my problem is when combining data with another table.
When I join Table B and Table A, I need to get the MAX for all Ids and in my query I dont know what Id is.
This is my query
SELECT B.*,C.*
JOIN A on A.Id = B.ID
JOIN C on A.id = B.ID
WHERE (SELECT MAX(COUNT)
FROM A
WHERE Id = <what goes here????>)
To summarise, what I want is Values from Table B, FileNumber from Table c (where the count is Max for ID from table A).
UPDATE: COrrecting table C above. Looks like I need Table A.
I think this is the query you're looking for:
select b.*, c.filenumber from b
join (
select id, max(count) as count from a
group by id
) as NewA on b.id = NewA.id
join c on NewA.count = c.count
However, you should take into account that I don't get why for id=1 in tableA you choose the 16 to match against table C (which is the max) and for id=2 in tableA you choose the 10 to match against table C (which is the min). I assumed you meant the max in both cases.
Edit:
I see you've updated tableA data. The query results in this, given the previous data:
+----+---------------+------------+
| ID | FILENAME | FILENUMBER |
+----+---------------+------------+
| 1 | sample1.file | 1234 |
| 2 | sample2.file | 3456 |
| 3 | sample3.file | 4567 |
+----+---------------+------------+
Here is a working example
Using Mosty’s working example (renaming the keyword count to cnt for a column name), this is another approach:
with abc as (
select
a.id,
a.cnt,
rank() over (
partition by a.id
order by cnt desc
) as rk,
b.filename
from a join b on a.id = b.id
)
select
abc.id, abc.filename, c.filenumber
from abc join c
on c.cnt = abc.cnt
where rk = 1;
select
PreMax.ID,
B.FileName,
C2.FileNumber
from
( select C.id, max( C.count ) maxPerID
from TableC C
group by C.ID
order by C.ID ) PreMax
JOIN TableC C2
on PreMax.ID = C2.ID
AND PreMax.maxPerID = C2.Count
JOIN TableB B
on PreMax.ID = B.ID