Sum values from 2 tables and subtract it to each other - sql-server

I have 2 tables, ord_tbl and pay_tbl. o_tbl with these data.
ord_tbl
invoice | emp_id | prod_id | amount
123 | 101 | 1 | 1000
123 | 101 | 2 | 500
123 | 101 | 3 | 500
124 | 101 | 2 | 300
125 | 102 | 3 | 200
pay_tbl
invoice | new_invoice | amount
123 | 321 | 300
123 | 322 | 200
124 | 323 | 300
125 | 324 | 100
I would like the selection statement to give me this result
invoice | emp_id | orig_amt | balance | status
123 | 101 | 2000 | 1500 | unsettled
The invoice that has 0 balance will not be included anymore. I know that I have to use the join and sub queries here but I don't even know how to start it! For me, as a beginner, this is very complex already. This is what I tried so far...
SELECT
ord_tbl.invoice,
SUM(ord_tbl.amount) As 'origAmt',
SUM(pay_tbl.amount) As 'payAmt',
origAmt - payAmt As 'bal'
FROM
ord_tbl
INNER JOIN pay_tbl
ON ord_tbl.invoice = pay_tbl.invoice
WHERE
ord_tbl.emp_id = #emp_id AND
bal != 0
GROUP BY
ord_tbl.invoice

You need to prepare your data joining on a non unique field will lead to a cross JOIN,that`s why you get 4000
;WITH CTE as
(SELECT ot.invoice,MAX(ot.emp_id) as emp_id,SUM(ot.amount) as origAmt FROM ord_tbl ot GROUP BY ot.invoice),
CTE2 as
( SELECT pt.invoice,SUM(pt.ammount) as payAmt FROM pay_tbl pt GROUP BY pt.invoice)
SELECT CTE.invoice,CTE.emp_id,CTE.origAmt,CTE.origAmt-NULLIF(CTE2.payAmt,0) as bal,'unsettled' as status
FROM
CTE LEFT JOIN CTE2 ON CTE.invoice=CTE2.invoice
AND CTE.emp_id=101 AND CTE.origAmt-NULLIF(CTE2.payAmt,0)>0

Related

Create rows for missing values in SQL (data preparation for decision tree)

Currently, my table looks like this
id | valName | valCount | type
123 | abb | 3 | 2
123 | abc | 2 | 2
123 | b | 5 | 2
251 | aaa | 2 | 1
251 | ab | 2 | 1
251 | abb | 2 | 1
251 | ac | 2 | 1
and so on.
I want to fill in missing valNames for every id and set valCount to 0. If my set of distinct valName was (aaa, aab, ab, abb, abc, ac, b) it would look like this.
id | valName | valCount | type
123 | aaa | 0 | 2
123 | aab | 0 | 2
123 | ab | 0 | 2
123 | abb | 3 | 2
123 | abc | 2 | 2
123 | ac | 0 | 2
123 | b | 5 | 2
251 | aaa | 2 | 1
251 | aab | 0 | 1
251 | ab | 2 | 1
251 | abb | 2 | 1
251 | abc | 0 | 1
251 | ac | 2 | 1
251 | b | 0 | 1
Also, the dataset is quite large. So efficient query is better.
As Dale suggested, this is my attempt. TABLE in the code is the table I am using.
select C.id, C.valName, C.type, COALESCE(D.valCount,0 ) as count
from (
select *
from (select id, min(type) as type
From TABLE
Group by id
) B
cross join
(select distinct valName FROM TABLE) A
) C
left join TABLE D
on C.id = D.id
and C.valName = D.valName
order by C.id
The idea behind this query is to create the id/valname table using cross join and then get valCount using left join.
This query works but is too slow.
Something like this
with unq_id_type_cte(id, [type]) as (
select distinct id, [type] from mytable)
insert mytable(id, valName, valCount, [type])
select uitc.id, t.v, 0, uitc.[type]
from
(values ('aaa'),('aab'),('ab'),('abb'),('abc'),('ac'),('b')) t(v)
cross join
unq_id_type_cte uitc
where not exists
(select 1 from mytable t_in where uitc.id=t_in.id
and t.v=t_in.valName);
If there are performance issues or concerns then the first thing to try imo would be to insert the cte into an indexed temp table.

Sum up articles per orderID via SQL Server

I have the following:
OrderID | Articlenumber|
--------+--------------+
1 | 123 |
2 | 222 |
1 | 799 |
1 | 987 |
2 | 444 |
3 | 212 |
2 | 222 |
1 | 898 |
and I want the following (sum up all article numbers per orderID):
orderID|articelnumber |
-------+--------------------+
1 |123, 799, 987, 898 |
2 |222, 444, 222 |
3 |212 |
or:
orderID|articelnumber|articelnumber|articelnumber |articelnumber |
-------+-------------+-------------+--------------+--------------+
1 |123 |799 |987 | 898 |
2 |222 |444 |222 | |
3 |212 | | | |
How can I do it with SQL-Server? The number of articles per orderID is variable.
Thanks a lot!
Is something like this what you want?
;WITH cte AS
(
SELECT OrderID, Articlenumber
FROM [YOUR_TABLE]
)
SELECT
OrderID,
STUFF((SELECT ',' + Articlenumber FROM [YOUR_TABLE] WHERE [YOUR_TABLE].OrderID = cte.OrderID FOR XML PATH('')), 1, 1, '') articelnumber
FROM cte
GROUP BY OrderID
ORDER BY 1

How can I get SQL results that resemble a pivot table

I am trying to get back some results from a database and it needs to be returned in a very specific way, similar to a pivot table but from what I've been reading about them I don't know if it applies since I do not need an aggregate. This is to display RFQ information in a report. The database structure is complicated but I will use a simplified example.
RFQ table
----------------
RFQID | Requestor
----------------
555 | 789
777 | 789
Item table
--------------------------
RFQID | Line | ItemID
--------------------------
555 | 1 | P07
777 | 1 | P07
ItemQuantity table
------------------------------------
RFQID | Line | Quantity | Unit
------------------------------------
555 | 1 | 5 | In
555 | 1 | 10 | In
555 | 1 | 15 | In
777 | 1 | 30 | In
777 | 1 | 50 | In
Vendor table
-----------------------------
RFQID | Line | VendorID
-----------------------------
555 | 1 | Speedy
777 | 1 | SlowPoke
So if I enter a query like this:
SELECT M.RFQID, Requester, I.Line, ItemID, Quantity, Unit, VendorID
FROM RFQ M
JOIN Item I ON M.RFQID = I.RFQID
JOIN Vendor V ON I.Line = V.Line AND I.RFQID = V.RFQID
JOIN ItemQuantity IQ ON IQ.Line = V.Line AND IQ.RFQID = V.RFQID
WHERE ItemID IN ('P07', 'P08')
ORDER BY M.RFQID DESC
Which will produce:
555 | 789 | 1 | P07 | 5 | In | Speedy
555 | 789 | 1 | P07 | 10 | In | Speedy
555 | 789 | 1 | P07 | 15 | In | Speedy
777 | 789 | 1 | P07 | 30 | In | SlowPoke
777 | 789 | 1 | P07 | 50 | In | SlowPoke
I need to get:
RFQID| Requestor | Line | ItemID | Quantity row1 | Unit row1 | Quantity row2 | Unit row2 | Quantity row3 | Unit row3 | VendorID
555 | 789 | 1 | P07 | 5 | In | 10 | In | 15 | In | Speedy
777 | 789 | 1 | P07 | 30 | In | 50 | In | NULL | NULL | SlowPoke
Is this possible? I know I can write a SP using a cursor but I wish to avoid that route if possible.
EDIT: This is part of a report that:
1) The whole company uses
2) Consists of 14 datasets
3) Is so customized that I doubt there is a report writer that can produce it.
4) It exists in Access 2003 which we wish to get rid of
5) I have almost the whole thing done with the PrintDocument with all the attributes table based so it can be modified without compiling anything.
Using Jason's technique I was able to produce the correct output.
SELECT M.RFQID, Requester, I.Line, ItemID, Qty1, Qty2, Qty3, VendorID
FROM RFQ M
JOIN Item I ON M.RFQID = I.RFQID
JOIN Vendor V ON I.Line = V.Line AND I.RFQID = V.RFQID
JOIN (SELECT RFQID, LineItem, MAX(CASE WHEN QtyID = 1 THEN QUANTITY END) AS Qty1, MAX(CASE WHEN QtyID = 2 THEN QUANTITY END) AS Qty2, MAX(CASE WHEN QtyID = 3 THEN QUANTITY END) AS Qty3
FROM ItemQuantity
GROUP BY RFQID, Line) IQ ON IQ.Line = I.Line AND IQ.RFQID = M.RFQID
WHERE ItemID IN ('P07', 'P08')
ORDER BY M.RFQID DESC
Which results in
RFQID| Requestor | Line | ItemID | Quantity row1 | Quantity row2 | Quantity row3 | VendorID
555 | 789 | 1 | P07 | 5 | 10 | 15 | Speedy
777 | 789 | 1 | P07 | 30 | 50 | NULL | SlowPoke
Although my demo data did not include a QtyID the table can be rewritten as:
ItemQuantity table
----------------------------------------------
RFQID | Line | QtyID | Quantity | Unit
----------------------------------------------
555 | 1 | 1 | 5 | In
555 | 1 | 2 | 10 | In
555 | 1 | 3 | 15 | In
777 | 1 | 1 | 30 | In
777 | 1 | 2 | 50 | In
The Unit can be added the same way.
Thanks Jason!

Need help based on conditions in SQL Server

I have a question about SQL Server.
Table patient:
pn | code | date | doctorcode
---------------------------------------
1 | 10 |2015-02-19 | 100
1 | 10 |2015-02-19 | 101
1 | 10 |2015-02-19 | 102
2 | 10 |2015-02-12 | 101
2 | 10 |2015-02-13 | 102
2 | 10 |2015-02-14 | 103
3 | 10 |2015-02-15 | 103
3 | 10 |2015-02-18 | 104
3 | 10 |2015-02-26 | 105
Table Patientref:
pn | code | sdate | edate | Status
-------------------------------------------------
1 | 10 |2015-02-13 | 2015-02-19 | 1
1 | 10 |2015-02-19 | 2015-03-24 | 2
1 | 10 |2015-04-28 | 2015-05-08 | 4
2 | 10 |2015-02-08 | 2015-02-19 | 4
2 | 10 |2015-02-09 | 2015-02-19 | 2
2 | 10 |2015-02-10 | 2015-02-19 | 2
2 | 10 |2015-02-11 | 2015-02-18 | 1
3 | 10 |2015-02-10 | 2015-02-17 | 4
3 | 10 |2015-02-10 | 2015-02-17 | 3
3 | 10 |2015-02-11 | 2015-02-18 | 3
2 | 10 |2015-04-10 | 2015-05-19 | 2
3 | 10 |2015-02-11 | 2015-02-18 | 1
3 | 10 |2015-02-26 | 2015-03-18 | 1
Here we need consider patient dates that fall between sdate and edate of the patientrefs table, and then we need to consider the highest status values in order (for example, the highest values in order - 2 is first highest, 4 is second highest, 3 is third highest, and 1 is fourth highest value)
If the date falls between multiple different sdate and edate with the same status values, then we need to consider the latest sdate value and from that entire record we need to extract that value.
Examples: patient
pn | code | date | doctorcode
2 | 10 |2015-02-12 | 101
2 | 10 |2015-02-13 | 102
2 | 10 |2015-02-14 | 103
Table : Patientref:
pn | code | sdate | edate | Status
2 | 10 |2015-02-08 | 2015-02-19 | 4
2 | 10 |2015-02-09 | 2015-02-19 | 2
2 | 10 |2015-02-10 | 2015-02-19 | 2
2 | 10 |2015-02-11 | 2015-02-18 | 1
Here, pn=2 values have dates which fall between sdate and edate of patientref table. Then we give highest values status is 2, and status 2 values have two records, then we go for max sdate(latest sdate). Then this pn=2 latest sdates is 2015-02-10 and we need to retrieve the corresponding edate and status values.
Based on this, the desired output is below:
pn | code | date | doctorcode | sdate |edate |status
1 | 10 |2015-02-19 | 100 |2015-02-19 |2015-03-24 | 2
1 | 10 |2015-02-19 | 101 |2015-02-19 |2015-03-24 | 2
1 | 10 |2015-02-19 | 102 |2015-02-19 |2015-03-24 | 2
2 | 10 |2015-02-12 | 101 |2015-02-10 |2015-02-19 | 2
2 | 10 |2015-02-13 | 102 |2015-02-10 |2015-02-19 | 2
2 | 10 |2015-02-14 | 103 |2015-02-10 |2015-02-19 | 2
3 | 10 |2015-02-15 | 103 |2015-02-10 |2015-02-17 | 4
3 | 10 |2015-02-18 | 104 |2015-02-11 |2015-02-18 | 3
3 | 10 |2015-02-26 | 105 |2015-02-26 |2015-03-18 | 1
I tried it like this:
select
a.pn, a.code, a.doctorcode, a.date,
b.sdate, b.edate, b.status
from
patient a
left join
(select
b.pn, b.code, b.sdate, b.edate,
row_number() over (partition by pn, org
order by case when status=2 then 1 when status=4 then 2 when status=3 then 3 when status=1 then 4 end desc,sdate desc) as rn
from patientref) b on a.pn = b.pn and a.code = b.code
and a.rn = 1
and a.date between b.sdate and b.edate
But it does not give the expected result. How can I write the query to achieve this task in SQL Server?
First off, to handle the status sorting you should really have a table in your system showing how they can be sorted. This would just be a table that has the status ID and a sort order column showing sorting priority. However, for your query you can just create a table variable to manage it.
declare #statuses table
([status] int,
sort_order int)
insert into #statuses ([status], sort_order) values (2,0);
insert into #statuses ([status], sort_order) values (4,1);
insert into #statuses ([status], sort_order) values (3,2);
insert into #statuses ([status], sort_order) values (1,3);
Then you can use CROSS APPLY to query your patient table and use the highest priority record from your patientref table:
select
p.pn,
p.code,
p.date,
p.doctorcode,
ca.sdate,
ca.edate,
ca.status
from patient p
cross apply
(select
top 1
pr.pn,
pr.code,
pr.sdate,
pr.edate,
pr.status
from patientref pr
inner join #statuses s on pr.status = s.status
where pr.pn = p.pn
and pr.code = p.code
and p.date between pr.sdate and pr.edate
order by s.sort_order, pr.sdate desc) as ca

Sql Query Group a range of numbers with low's and high's

I have a table that looks something like below
street_id | address_number | address_direction | address_street | address_type ...
----------------------------------------------------------------------------------
1 | 121 | W | Fake | St
1 | 131 | W | Fake | St
1 | 200 | W | Fake | St
2 | 321 | N | Fake | St
2 | 131 | N | Fake | St
2 | 500 | N | Fake | St
Can anyone help me come up with a query that would help me display the data like below?
street_id | address_low_range | address_high_range | address_direction | address_street | address_type ...
----------------------------------------------------------------------------------
1 | 121 | 200 | W | Fake | St
2 | 131 | 500 | N | Fake | St
Thanks in advance
you can use Min(address_number) and Max(address_number) to select the low and high ranges, then group by street_id, address_direction, address_street, address_type:
SELECT
street_id,
Min(address_number) as address_low_range,
Max(address_number) as address_high_range,
address_direction,
address_street,
address_type
FROM table_name
GROUP BY street_id, address_direction, address_street, address_type
select
street_id,
direction,
min(address_number) as address_low_range,
max(address_number) as address_high_range
address_street,
address_type
from mytable
group by address_id, address_street, direction;
By the way, why are address_street and address_type in your table when assumably, that's what street_id refers to?
Off the top of my head:
select street_id, min(address_number), max(address_number), address_direction
from addresses
group by street_id, address_direction
Can you test this query
select street_id, MIN(address_number) add_low,
MAX(address_number) add_high FROM your_table
Group by street_id
Use the MIN() and MAX() aggregate functions on the columns you want the min or max of. Make sure to include the other columns in a GROUP BY clause.

Resources