I'm trying to calculate stock in sql. I've 3 table product, purchase and sales.
product table is
+----+------------------+
| id | product_name |
+----+------------------+
| 1 | apple |
|----|------------------|
| 2 |banana |
|----|------------------|
| 3 |mango |
+----+------------------+
Now color table
+----+------------------+
| id | color_name |
+----+------------------+
| 1 | dark |
|----|------------------|
| 2 | light |
+----|------------------+
purchase table is
+-------+-------------+
| id | quantity |color
+-------+-------------+
| 1 | 15 |dark
+-------+-------------+
| 1 | 10 |light
+-------+-------------+
| 2 | 5 |dark
+-------+-------------+
| 3 | 25 |light
+-------+-------------+
and sales table is
+-------+-------------+
| id | quantity |color
+-------+-------------+
| 1 | 5 |dark
+-------+-------------+
| 1 | 5 |light
+-------+-------------+
| 2 | 5 |dark
+-------+-------------+
| 3 | 5 |light
+-------+-------------+
Purchase and sales table have foreign key id references id of product table. Now I'm trying to calculate stock available i.e. difference of purchase and sales in below format on the basis of color too
+----+------------------+-------------+
| id | product_name | quantity |color
+----+------------------+-------------+
| 1 | apple | 10 |dark
|----|------------------|-------------|
| 1 | apple | 5 |light
|----|------------------|-------------|
| 2 |banana | 0 |dark
|----|------------------|-------------|
| 3 |mango | 20 |light
+----+------------------+-------------+
You may looking for this
SELECT p.id,p.Name,Purchase.purchaseQty-sales.salseQty as totalQty
FROM Product p
OUTER APPLY(
SELECT purchase.id, SUM(purchase.quantity) purchaseQty
FROM purchase
where purchase.id= p.id
GROUP BY purchase.id
)Purchase
OUTER APPLY(
SELECT sales.id, SUM(sales.quantity) salseQty
FROM sales
where sales.id= p.id
GROUP BY sales.id
)sales
SELECT a.id, a.product_name,
SUM(b.quantity) - SUM(c.quantity) as 'quantity'
FROM product a
LEFT JOIN purchase b
ON a.id=b.id
LEFT JOIN sales c
ON b.id = c.id group by a.id, a.product_name
(I didn't test anything)
Try this solution with outer apply.
drop table if exists dbo.tProduct;
drop table if exists dbo.tPurchase;
drop table if exists dbo.tSale;
create table dbo.tProduct (
id int
, product_name varchar(100)
);
create table dbo.tPurchase (
id int
, quantity int
);
create table dbo.tSale (
id int
, quantity int
);
insert into dbo.tProduct (id, product_name)
values (1, 'apple'), (2, 'banana'), (3, 'mango');
insert into dbo.tPurchase (id, quantity)
values (1, 15), (2, 10), (1, 5), (3, 25);
insert into dbo.tSale (id, quantity)
values (1, 5), (3, 10), (1, 5), (3, 5), (2, 5);
select
p.id
, p.product_name
, pur.Quantity - sal.Quantity as Quantity
from dbo.tProduct p
outer apply (
select
sum(tp.quantity) as Quantity
from dbo.tPurchase tp
where p.id = tp.id
) pur
outer apply (
select
sum(tp.quantity) as Quantity
from dbo.tSale tp
where p.id = tp.id
) sal
Related
I have a larger Database with Times that employees entered. They enter an activity, when it was and how long they spent on it, as well as a customer.
I'm now trying to return a table with all employees, that Sums their times, but only if it's timed for a subset of Customers. I can get either a table with The Correct times, but employees that didn't enter any time are omitted, or I get all employees but with the sum time from all customers.
The tables I have are:
EMPLOYEE for the employees
ACTIVITY for all activities
CUSTOMER for the customers
To have some "example Data":
| EMPLOYEE | | ACTIVITY |
+------------+---------+ +------------+------------+------------+
| I_EMPLOYEE | S_NAME1 | | I_EMPLOYEE | I_CUSTOMER | N_DURETIME |
+------------+---------+ +------------+------------+------------+
| 1 | A | | 1 | 1 | 5 |
| 2 | B | | 2 | 3 | 10 |
| 3 | C | | 1 | 3 | 15 |
+------------+---------+ | 3 | 2 | 10 |
| 1 | 2 | 10 |
+------------+------------+------------+
What i'd expect to get when i want all times except Customer 2:
+----------+----------+
| EMPLOYEE | DURETIME |
+----------+----------+
| 1 | 20 |
| 2 | 10 |
| 3 | - |
+----------+----------+
I get either of those two out:
+----------+----------+ +----------+----------+
| EMPLOYEE | DURETIME | | EMPLOYEE | DURETIME |
+----------+----------+ +----------+----------+
| 1 | 20 | | 1 | 30 |
| 2 | 10 | | 2 | 10 |
+----------+----------+ | 3 | 10 |
+----------+----------+
To get the correct times i use the following:
SELECT emp.S_NAME1 AS Mitarbeiter, SUM(act.N_DURETIME)/60 as Zeit
FROM EMPLOYEE AS emp
LEFT JOIN ACTIVITY AS act on act.I_EMPLOYEE = emp.I_EMPLOYEE
LEFT JOIN CUSTOMER AS cust on cust.I_CUSTOMER = act.I_CUSTOMER
WHERE cust.CUSTNO NOT '2'
to get the full list of employees i used:
SELECT emp.S_NAME1 AS Mitarbeiter, SUM(act.N_DURETIME)/60 as Zeit
FROM EMPLOYEE AS emp
LEFT JOIN ACTIVITY AS act on act.I_EMPLOYEE = emp.I_EMPLOYEE
LEFT JOIN CUSTOMER AS cust on cust.I_CUSTOMER = act.I_CUSTOMER AND cust.CUSTNO NOT '2'
So, depending on whether I put my "Customer Filter" in the JOIN or the WHERE statement, I get half of the correct table. How can I combine those to get the correct output?
Create Table #emp
(
i_emp Int,
s_name1 Char(1)
)
Insert Into #emp Values
(1,'A'),
(2,'B'),
(3,'C')
Create Table #Activity
(
i_emp Int,
i_cust Int,
n_duretime Int
)
Insert Into #Activity Values
(1,1,5),
(2,3,10),
(1,3,15),
(3,2,10),
(1,2,10)
Query
Select
e.i_emp,
Sum(Case When a.i_cust = 2 Then Null Else a.n_duretime End) As durationTot
From
#emp e Left Join
#Activity a On e.i_emp = a.i_emp
Group By
e.i_emp
Result:
i_emp durationTot
1 20
2 10
3 NULL
You can try the following query
create table Employee(I_EMPLOYEE int, S_NAME1 char(1))
insert into Employee Values (1, 'A'),(2, 'B'),(3, 'C')
create table ACTIVITY (I_EMPLOYEE int, I_CUSTOMER int, N_DURETIME int)
insert into ACTIVITY Values(1, 1, 5 ),( 2, 3, 10), (1, 3, 15), ( 3, 2, 10), ( 1 , 2 , 10 )
select EMPLOYEE, sum(isnull(DURETIME, 0)) as DURETIME from(
select EMPLOYEE.S_NAME1 as EMPLOYEE, case I_Customer when 2 then 0 else N_DURETIME end as DURETIME from activity
inner join Employee on activity.I_EMPLOYEE = Employee.I_EMPLOYEE
)a group by EMPLOYEE
Below is the output
I_EMPLOYEE EMPLOYEE DURETIME
--------------------------------
1 A 20
2 B 10
3 C 0
Please check http://sqlfiddle.com/#!18/1acd1/2
I'm passing UserId to get User's countryid so I can fetch plans according to user's country.
I have plans specific to countries but I don't have plans for all countries so when I pass userid where I don't have any plan for that user/country at the moment no rows returns but in that case I need to show default plans (plans with country id = '1') to that user.
Please see fiddle, In case of userid='4' no rows returns so in that case I need to show default plans to that user. all default plans have countryid=1
I hope you understand my question :) thanks
Here is my take on it.
I took a different approach and started from the tblProfile because your input for this query is actually a UserId so I found it more logical to follow this process:
For the UserId, fetch PlanId of his CountryId
If User's CountryId does not exist in lstCountry or has no PlanId replace User's CountryId by the default CountryId=1 (as mentioned in the comments below)
Finally, fetch Plan details and Currency for the final CountryId
Tables definitions
CREATE TABLE lstCountry(
CountryID int,
CountryTitle varchar(100),
CurrencyCode varchar(3))
INSERT INTO lstCountry (CountryId, CountryTitle, CurrencyCode) VALUES
(1, 'USA', 'USD'),
(2, 'GB', 'GBP'),
(3, 'France', 'EUR')
CREATE TABLE tblProfile(
UserId int,
CountryId int)
INSERT INTO tblProfile (UserId, CountryId) VALUES
(1, 1),
(2, 2),
(3, 3),
(4, 4)
CREATE TABLE tblPlan (
PlanId int,
PlanTitle nvarchar(50),
PlanPrice money,
CountryId int)
INSERT INTO tblPlan (PlanId, PlanTitle, PlanPrice, CountryId) VALUES
(1, 'PlanDefault', 10, 1),
(2, 'Plan2', 20, 2),
(3, 'Plan3', 30, 3),
(4, 'PlanDefault', 40, 1),
(5, 'Plan5', 50, 2)
Query
select distinct f.PlanId,
f.PlanTitle,
f.PlanPrice,
e.CurrencyCode
from (
select case when c.PlanId is null then '1' else a.CountryId end as CountryId
from tblProfile a
left join lstCountry b on a.CountryId=b.CountryId
left join tblPlan c on b.CountryId=c.CountryId
where UserId=4
) d
inner join lstCountry e on d.CountryId=e.CountryId
inner join tblPlan f on d.CountryId=f.CountryId
Result for UserId=1:
| PlanId | PlanTitle | PlanPrice | CurrencyCode |
|--------|-------------|-----------|--------------|
| 1 | PlanDefault | 10 | USD |
| 4 | PlanDefault | 40 | USD |
Result for UserId=2
| PlanId | PlanTitle | PlanPrice | CurrencyCode |
|--------|-----------|-----------|--------------|
| 2 | Plan2 | 20 | GBP |
| 5 | Plan5 | 50 | GBP |
Result for UserId=3
| PlanId | PlanTitle | PlanPrice | CurrencyCode |
|--------|-----------|-----------|--------------|
| 3 | Plan3 | 30 | EUR |
Result for UserId=4
| PlanId | PlanTitle | PlanPrice | CurrencyCode |
|--------|-------------|-----------|--------------|
| 1 | PlanDefault | 10 | USD |
| 4 | PlanDefault | 40 | USD |
Here is the SQL Fiddle
http://sqlfiddle.com/#!18/7068c/8/0
I think a LEFT JOIN and second parse of the table might achieve this. If not, then please look at #JuanCarlosOropeza comments and update your post with Sample and expected data.
SELECT p.PlanId,
p.PlanTitle,
p.PlanPrice,
ISNULL(c.CurrencyCode, d.CurrencyCode) AS CurrencyCode
FROM tblPlan p
LEFT JOIN lstCountry c ON c.CountryId = p.CountryId
LEFT JOIN lstCountry d ON c.CountryId IS NULL
AND d.CountryId = 226
WHERE P.CountryId = (SELECT sq.CountryId
FROM tblProfile sq --Not sure if this requires a Default Country
WHERE UserId = #UserId)
AND p.IsVisible = '1';
I have a postgresql schema with two tables:
tableA: tableB:
| id | username | | fk_id | resource |
| 1 | user1 | | 2 | item1 |
| 2 | user1 | | 1 | item3 |
| 3 | user1 | | 1 | item2 |
| 4 | user2 | | 4 | item5 |
| 5 | user2 | | 5 | item8 |
| 6 | user3 | | 3 | item9 |
The foreign key fk_id in tableB references id in tableA.
How can I update all of the foreign key id's of tableB to point to the lowest entry for a unique username in tableA?
update table_b b
set fk_id = d.id
from table_a a
join (
select distinct on (username) username, id
from table_a
order by 1, 2
) d using(username)
where a.id = b.fk_id;
Test it here.
The query used inside the update gives actual_id, username, desired_id:
select a.id actual_id, username, d.id desired_id
from table_a a
join (
select distinct on (username) username, id
from table_a
order by 1, 2
) d using(username)
actual_id | username | desired_id
-----------+----------+------------
1 | user1 | 1
2 | user1 | 1
3 | user1 | 1
4 | user2 | 4
5 | user2 | 4
6 | user3 | 6
(6 rows)
We define your tables:
CREATE TABLE tableA (id, username) AS
SELECT * FROM
(
VALUES
(1, 'user1'),
(2, 'user1'),
(3, 'user1'),
(4, 'user2'),
(5, 'user2'),
(6, 'user2')
) AS x ;
CREATE TABLE tableB (fk_id, resource) AS
SELECT * FROM
(
VALUES
(2, 'item1'),
(1, 'item3'),
(1, 'item2'),
(4, 'item5'),
(5, 'item8'),
(3, 'item9')
) AS x ;
With that info, you can create a (virtual) conversion table, and use it to update your data:
-- Using tableA, make a new table with the
-- minimum id for every username
WITH username_to_min_id AS
(
SELECT
min(id) AS min_id, username
FROM
tableA
GROUP BY
username
)
-- Convert the previous table to a id -> min_id
-- conversion table
, id_to_min_id AS
(
SELECT
id, min_id
FROM
tableA
JOIN username_to_min_id USING(username)
)
-- Use this conversion table to update tableB
UPDATE
tableB
SET
fk_id = min_id
FROM
id_to_min_id
WHERE
-- JOIN condition with table to update
id_to_min_id.id = tableB.fk_id
-- Take out the ones that won't change
AND (fk_id <> min_id)
RETURNING
* ;
The result you would get is:
+-------+----------+----+--------+
| fk_id | resource | id | min_id |
+-------+----------+----+--------+
| 1 | item1 | 2 | 1 |
| 1 | item9 | 3 | 1 |
| 4 | item8 | 5 | 4 |
+-------+----------+----+--------+
Shows you that three rows have been updated, that had fk_id = (2, 3, 5), and have now (1, 1, 4). (The id is the "old" fk_id value).
You can check it at http://rextester.com/EQPH47434
You can "squeeze everything" [change every virtual table name by its definition, and do a couple of SELECT optimizations] and get this equivalent query (probably less clear, yet totally equivalent):
UPDATE
tableB
SET
fk_id = min_id
FROM
tableA
JOIN
(
SELECT
min(id) AS min_id, username
FROM
tableA
GROUP BY
username
) AS username_to_min_id
USING (username)
WHERE
tableA.id = tableB.fk_id
AND (fk_id <> min_id)
RETURNING
* ;
I need to write a statement joining two tables based on dates.
Table 1 contains time recording entries.
+----+-----------+--------+---------------+
| ID | Date | UserID | DESC |
+----+-----------+--------+---------------+
| 1 | 1.10.2010 | 5 | did some work |
| 2 | 1.10.2011 | 5 | did more work |
| 3 | 1.10.2012 | 4 | me too |
| 4 | 1.11.2012 | 4 | me too |
+----+-----------+--------+---------------+
Table 2 contains the position of each user in the company. The ValidFrom date is the date at which the user has been or will be promoted.
+----+-----------+--------+------------+
| ID | ValidFrom | UserID | Pos |
+----+-----------+--------+------------+
| 1 | 1.10.2009 | 5 | PM |
| 2 | 1.5.2010 | 5 | Senior PM |
| 3 | 1.10.2010 | 4 | Consultant |
+----+-----------+--------+------------+
I need a query which outputs table one with one added column which is the position of the user at the time the entry has been made. (the Date column)
All date fileds are of type date.
I hope someone can help. I tried a lot but don't get it working.
Try this using a subselect in the where clause:
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE TimeRecord
(
ID INT,
[Date] Date,
UserID INT,
Description VARCHAR(50)
)
INSERT INTO TimeRecord
VALUES (1,'2010-01-10',5,'did some work'),
(2, '2011-01-10',5,'did more work'),
(3, '2012-01-10', 4, 'me too'),
(4, '2012-11-01',4,'me too')
CREATE TABLE UserPosition
(
ID Int,
ValidFrom Date,
UserId INT,
Pos VARCHAR(50)
)
INSERT INTO UserPosition
VALUES (1, '2009-01-10', 5, 'PM'),
(2, '2010-05-01', 5, 'Senior PM'),
(3, '2010-01-10', 4, 'Consultant ')
Query 1:
SELECT TR.ID,
TR.[Date],
TR.UserId,
TR.Description,
UP.Pos
FROM TimeRecord TR
INNER JOIN UserPosition UP
ON UP.UserId = TR.UserId
WHERE UP.ValidFrom = (SELECT MAX(ValidFrom)
FROM UserPosition UP2
WHERE UP2.UserId = UP.UserID AND
UP2.ValidFrom <= TR.[Date])
Results:
| ID | Date | UserId | Description | Pos |
|----|------------|--------|---------------|-------------|
| 1 | 2010-01-10 | 5 | did some work | PM |
| 2 | 2011-01-10 | 5 | did more work | Senior PM |
| 3 | 2012-01-10 | 4 | me too | Consultant |
| 4 | 2012-11-01 | 4 | me too | Consultant |
You can do it using OUTER APPLY:
SELECT ID, [Date], UserID, [DESC], x.Pos
FROM table1 AS t1
OUTER APPLY (
SELECT TOP 1 Pos
FROM table2 AS t2
WHERE t2.UserID = t1.UserID AND t2.ValidFrom <= t1.[Date]
ORDER BY t2.ValidFrom DESC) AS x(Pos)
For every row of table1 OUTER APPLY operation fetches all table2 rows of the same user that have a ValidFrom date that is older or the same as [Date]. These rows are sorted in descending order and the most recent of these is finally returned.
Note: If no match is found by the OUTER APPLY sub-query then a NULL value is returned, meaning that no valid position exists in table2 for the corresponding record in table1.
Demo here
This works by using a rank function and subquery. I tested it with some sample data.
select sub.ID,sub.Date,sub.UserID,sub.Description,sub.Position
from(
select rank() over(partition by t1.userID order by t2.validfrom desc)
as 'rank', t1.ID as'ID',t1.Date as'Date',t1.UserID as'UserID',t1.Descr
as'Description',t2.pos as'Position', t2.validfrom as 'validfrom'
from temployee t1 inner join jobs t2 on -- replace join tables with your own table names
t1.UserID=t2.UserID
) as sub
where rank=1
This query would work
select t1.*,t2.pos from Table1 t1 left outer join Table2 t2 on
t1.Date=t2.Date and t1.UserID=t2.UserID
I have a question in sql server
table name : Emp
Id |Pid |Firstname| LastName | Level
1 |101 | Ram |Kumar | 3
1 |100 | Ravi |Kumar | 2
2 |101 | Jaid |Balu | 10
1 |100 | Hari | Babu | 5
1 |103 | nani | Jai |44
1 |103 | Nani | Balu |10
3 |103 |bani |lalu |20
Here need to retrieve unique records based on id and Pid columns and records which have duplicate records need to skip.
Finally I want output like below
Id |Pid |Firstname| LastName | Level
1 |101 | Ram |Kumar | 3
2 |101 | Jaid |Balu | 10
3 |103 |bani |lalu |20
I found duplicate records based on below query
select id,pid,count(*) from emp group by id,pid having count(*) >=2
this query get duplicated records 2 that records need to skip to retrieve output
please tell me how to write query to achieve this task in sql server.
Since your output is based on unique ID and PID which do not have any duplicate value, You can use COUNT with partition to achieve your desired result.
SQL Fiddle
Sample Data
CREATE TABLE Emp
([Id] int, [Pid] int, [Firstname] varchar(4), [LastName] varchar(5), [Level] int);
INSERT INTO Emp
([Id], [Pid], [Firstname], [LastName], [Level])
VALUES
(1, 101, 'Ram', 'Kumar', 3),
(1, 100, 'Ravi', 'Kumar', 2),
(2, 101, 'Jaid', 'Balu', 10),
(1, 100, 'Hari', 'Babu', 5),
(1, 103, 'nani', 'Jai', 44),
(1, 103, 'Nani', 'Balu', 10),
(3, 103, 'bani', 'lalu', 20);
Query
SELECT *
FROM
(
SELECT *,rn = COUNT(*) OVER(PARTITION BY ID,PID)
FROM Emp
) Emp
WHERE rn = 1
Output
| Id | Pid | Firstname | LastName | Level |
|----|-----|-----------|----------|-------|
| 1 | 101 | Ram | Kumar | 3 |
| 2 | 101 | Jaid | Balu | 10 |
| 3 | 103 | bani | lalu | 20 |