Select without - values from table SQL - sql

I'm taking items [I1] from this SQL Server table and returning to this table as - QTY and - TOTAL ..
http://i.stack.imgur.com/bdnPF.png
After that I have another row with the same Item ID and - QTY AND -Total
http://i.stack.imgur.com/4vUNP.png
I need to filter every time without returned product,
In this case I already returned the I1, when I'm going to return sale invoice .. I need to select the none returned products,
SELECT
DEL_PurchasesLines.ItemIdentityCode,
SUM(DEL_PurchasesLines.Qty) As Qty,
SUM(DEL_PurchasesLines.Total) As Total
FROM DEL_PurchasesLines
WHERE InvoiceNo = '1' AND DealerCode = 'S0002M'
GROUP BY
DEL_PurchasesLines.ItemIdentityCode
HAVING SUM(DEL_PurchasesLines.Total) > 0 AND SUM(DEL_PurchasesLines.Qty) > 0

I always like to create some test data in tempdb.
--
-- Create sample data
--
use tempdb;
go
if object_id('items') > 0
drop table items
go
create table items
(
id varchar(8),
qty int,
total int
);
go
insert into items values
('I1', 2, 4),
('I2', 3, 6),
('I3', 5, 10),
('I1', -2, -4);
go
select * from items;
go
One way to solve this problem is to group by the id, summing both the qty and total columns. Display only rows that have > 0.
--
-- Show lines in which qty * total > 0
--
select id, sum(qty) as sum_qty, sum(total) as sum_total
from items
group by id
having sum(qty) > 0 and sum(total) > 0;
Another way to think of this is to show all orders that do not have returns.
--
-- Show rows that do not have any returns
--
select *
from items i left join
(
select id
from items
where qty < 0 or total < 0
) r on i.id = r.id
where r.id is null

Related

How to set to update a column in a table that have more than one same value

I am trying to update a temporary table in a stored procedure, so in the set clause of the update, I used an inner join to update one column, but since that column has some same value the update gets repeated. How I can get the update only for the first unique value of the column?
Here is what I've done for the update:
select
style, color,item, prodnocompany, WIP, 0 As Inventory
into
#protable
from
prodorderheader poh
inner join
prodorderdetal pod on poh.prodno = pod.rpodno
select
item, sum(QOHQTY) as Inventory
into
#Inve
from
inventory
group by
item
update p
set p.Inventory += i.Inventory
from #prod p
inner join #Inve i on p.item = i.item
I need to get the following result:
Style
item
Inventory
D3623M
123776
12
D3623M
123665
11
T3445S
122099
10
D3565W
133422
12
D3565W
133422
0
In the #Prod table there may be some repeating of style and item, because they have a different prodnocompany, but in Inventory, each item has a specific quantity on the had amount (which is QOHQTY), but what I got from the update is as below:
Style
item
prodnocompany
Inventory
D3623M
123776
234
15
D3623M
123665
211
11
T3445S
122099
122
10
D3565W
133422
456
12
D3565W
133422
432
12
How can I update for the first item if I have the same item for different pronocompanys?
As an option, add the rank to the #prod table and then take only the first (last row) for updating.
For example, so:
declare
#t table (col varchar (100), i int)
declare
#t2 table (rn int, col varchar (100), i int)
insert into #t
select 'A', 100
union all
select 'B', 100
union all
select 'C', 200
union all
select 'C', 300
insert into #t2
select DENSE_RANK()over (partition by col order by i), * from #t
select * from #t2 t
where t.rn in (select min(t_s.rn) from #t2 t_s where t_s.col=t.col group by col)

Sum Quantity and Filter Results

I have the following table with order id's and quantities. I need to be able to sum the quantity and retrieve the id's that that equal less than the provided number.
| id | quantity |
|------|----------|
| 100 | 1 |
| 200 | 25 |
| 300 | 15 |
For example, I need the id's where the sum of quantity equals less than 25.
When I try the following it only provides me the first id (100).
Select *
from (
select *,
SUM (Quantity) OVER (ORDER BY Id) AS SumQuantity
from dbo.Orders
) as A
where SumQuantity <= 25
Is it possible to adjust this query where it will provide me id 100 and 300, since the sum total of those orders is less than 25?
I know I can use a where clause on for quantity less than 25, but the important thing here is I need to be able to sum the quantity and pull id's that give me less than the provided number.
Thank you in advance!
Perhaps you want to order by the quantity instead of id?
Select o.*
from (select o.*, SUM (Quantity) OVER (ORDER BY quantity) AS SumQuantity
from dbo.Orders
) o
where SumQuantity <= 25;
This chooses the smallest values so you will get the most rows.
Group by Id and set the condition in the HAVING clause:
select Id, SUM(Quantity) AS SumQuantity
from Orders
group by Id
having SUM(Quantity) <= 25
See the demo.
Results:
Id | SumQuantity
100 | 1
200 | 25
300 | 15
If you want to include all the columns you can modify your query to not ORDER BY id but PARTITION BY id:
select *
from (
select *,
SUM (Quantity) OVER (PARTITION BY Id) AS SumQuantity
from Orders
) as A
where SumQuantity <= 25
For this dataset:
CREATE TABLE Orders([id] varchar(6), [quantity] int);
INSERT INTO Orders([id], [quantity])VALUES
('100', '1'), ('100', '2'),
('200', '25'), ('200', '3'),
('300', '15'), ('300', '5');
Results:
id | quantity | SumQuantity
100 | 1 | 3
100 | 2 | 3
300 | 15 | 20
300 | 5 | 20
See the demo.
Setup:
Your threshold can vary, so let's make it into a variable:
declare #threshold int = 25;
But I also imagine that your table values can vary, like if we add another row only having a quantity of 2:
declare #orders table (id int, quantity int)
insert #orders values (100,1), (200,25), (300,15), (400, 2);
Solution:
For this, we'll need a recursive kind of cross joining:
with
traverse as (
select ids = convert(nvarchar(255), id),
id,
quantity
from #orders
where quantity < #threshold
union all
select ids =
convert(nvarchar(255), tv.ids + ',' +
convert(nvarchar(255), o.id)),
o.id,
quantity = tv.quantity + o.quantity
from traverse tv
cross join #orders o
where tv.id < o.id
and tv.quantity + o.quantity < #threshold
)
select t.ids, t.quantity
from traverse t;
which will produce:
Explanation:
The above code is an algorithm that builds a tree. It starts with your base id's and quantities as nodes (the anchor part of the CTE). It trims anything not meeting the threshold.
It then adds edges by cross joining with orders table again (the recursive part of the CTE), but it only includes the following:
Id's that are greater than the last id considered in the current node (this is so that we avoid duplicate considerations, such as ids = '300,400' and ids = '400,300').
Ids where the sum of quantities is less than the threshold.
Warnings:
But beware, the type of problem you're considering will have computational complexity considerations. But because of the trimming conditions, it will be more efficient than doing all the cross joins first and then filtering the result set at the end.
Also, keep in mind that you may get rows in your table where there is no single set of numbers that will sum up to less than 25. Rather, you can get different paths to that sum. The way I produce the results here will help you identify such a situation.
cross join is perfect for this task, try:
declare #tbl table (id int, quantity int);
insert into #tbl values
(100, 1), (200, 25), (300, 15), (400, 10);
select distinct case when t1.id > t2.id then t1.id else t2.id end,
case when t1.id < t2.id then t1.id else t2.id end
from #tbl t1
cross join #tbl t2
where t1.id <> t2.id
and t1.quantity + t2.quantity < 25

how to get some records in first row in sql

I have q query like this:
Select WarehouseCode from [tbl_VW_Epicor_Warehse]
my output looks like this
WarehouseCode
Main
Mfg
SP
W01
W02
W03
W04
W05
But sometimes I want to get W04 as the first record, sometimes I want to get W01 as the first record .
How I can write a query to get some records in first row??
Any help is appreciated
you could try and select the row with the code you want to appear first by specifying a where condition to select that row alone then you can union all another select with all other rows that doesn't have this name
as follows
SELECT WarehouseCode FROM Table WHERE WarehouseCode ='W04'
UNION ALL
SELECT WarehouseCode FROM Table WHERE WarehouseCode <>'W04'
Use a parameter to choose the top row, which can be passed to your query as required, and sort by a column calculated on whether the value matches the parameter; something like the ORDER BY clause in the following:
DECLARE #Warehouses TABLE (Id INT NOT NULL, Label VARCHAR(3))
INSERT #Warehouses VALUES
(1,'W01')
,(2,'W02')
,(3,'W03')
DECLARE #TopRow VARCHAR(3) = 'W02'
SELECT *
FROM #Warehouses
ORDER BY CASE Label WHEN #TopRow THEN 1 ELSE 0 END DESC
May be you don't need to store this list in table? And you want something like this?
SELECT * FROM (VALUES ('WarehouseCode'),
('Main'),
('Mfg'),
('SP'),
('W01'),
('W02'),
('W03'),
('W04'),
('W05')) as v(s)
Here you can change order manually as you want.
As commented by #ankit bajpai
You are looking for Custom sorting that is achieve by CASE with ORDER BY statement
Whenever you want WAo4 on top you can use
ORDER BY Case When col = 'W04' THEN 1 ELSE 2 END
Example below:
Select col from
(
select 'Main' col union ALL
select 'Mfg' union ALL
select 'SP' union ALL
select 'W01' union ALL
select 'W02' union ALL
select 'W03' union ALL
select 'W04' union ALL
select 'W05'
) randomtable
ORDER BY Case When col = 'W04' THEN 1 ELSE 2 END
EDIT: AFTER MARKED AS ANSWER
IN support of #Maha Khairy because that IS MARKED AS ANSWER and the only answer which is DIFFRENT
rest all are pushing OP to use "ORDER by with case statements"
let`s use UNION ALL APPROCH
create table #testtable (somedata varchar(10))
insert into #testtable
Select col from
(
select 'W05' col union ALL
select 'Main' union ALL
select 'Mfg' union ALL
select 'SP' union ALL
select 'W01' union ALL
select 'W02' union ALL
select 'W03' union ALL
select 'W04'
) randomtable
Select * From #testtable where somedata = 'W04'
Union ALL
Select * From #testtable where somedata <> 'W04'
The result set is rendering data to the grid as requested the OP
idia is to get first all rows where equal to 'W04' is and then
not equal to 'W04' and then concatinate the result. so that rows 'W04'
will always be on the top because its used in the query first, fair enough.
, but that is not the only point to use (custom sorting/sorting) in that fasion there is one another
and a major one that is PERFORMANCE
yes
"case with order by" will never able to take advantages of KEY but Union ALL will be, to explore it more buld the test table
and check the diffrence
CREATE TABLE #Orders
(
OrderID integer NOT NULL IDENTITY(1,1),
CustID integer NOT NULL,
StoreID integer NOT NULL,
Amount float NOT NULL,
makesrowfat nchar(4000)
);
GO
-- Sample data
WITH
Cte0 AS (SELECT 1 AS C UNION ALL SELECT 1), --2 rows
Cte1 AS (SELECT 1 AS C FROM Cte0 AS A, Cte0 AS B),--4 rows
Cte2 AS (SELECT 1 AS C FROM Cte1 AS A ,Cte1 AS B),--16 rows
Cte3 AS (SELECT 1 AS C FROM Cte2 AS A ,Cte2 AS B),--256 rows
Cte4 AS (SELECT 1 AS C FROM Cte3 AS A ,Cte3 AS B),--65536 rows
Cte5 AS (SELECT 1 AS C FROM Cte4 AS A ,Cte2 AS B),--1048576 rows
FinalCte AS (SELECT ROW_NUMBER() OVER (ORDER BY C) AS Number FROM Cte5)
INSERT #Orders
(CustID, StoreID, Amount)
SELECT
CustID = Number / 10,
StoreID = Number % 4,
Amount = 1000 * RAND(Number)
FROM FinalCte
WHERE
Number <= 1000000;
GO
lets now do the same for custid "93190"
Create NONclustered Index IX_CustID_Orders ON #Orders (CustID)
INCLUDE (OrderID ,StoreID, Amount ,makesrowfat )
WARM CHACHE RESULTS
SET STATISTICS TIME ON
DECLARE #OrderID integer
DECLARE #CustID integer
DECLARE #StoreID integer
DECLARE #Amount float
DECLARE #makesrowfat nchar(4000)
Select #OrderID =OrderID ,
#CustID =CustID ,
#StoreID =StoreID ,
#Amount =Amount ,
#makesrowfat=makesrowfat
FROM
(
Select * From #Orders where custid =93190
Union ALL
Select * From #Orders where custid <>93190
)TLB
**
--elapsed time =2571 ms.
**
DECLARE #OrderID integer
DECLARE #CustID integer
DECLARE #StoreID integer
DECLARE #Amount float
DECLARE #makesrowfat nchar(4000)
Select #OrderID =OrderID ,
#CustID =CustID ,
#StoreID =StoreID ,
#Amount =Amount ,
#makesrowfat=makesrowfat
From #Orders
ORDER BY Case When custid = 93190 THEN 1 ELSE 2 END
elapsed time = 70616 ms
**
UNION ALL performance 2571 ms. ORDER BY CASE performance
70616 ms
**
UNION ALL is a clear winner ORDER BY IS not ever nearby in performance
BUT we forgot that SQL is declarative language,
we have no direct control over how data has fetch by the sql, there is a software code ( that changes with the releases)
IN between
user and sql server database engine, which is SQL SERVER OPTIMIZER that is coded to get the data set
as specified by the USER and its has responsibility to get the data with least amount of resources. so there are chances
that you wont get ALWAYS the result in order until you specify ORDER BY
some other references:
#Damien_The_Unbeliever
Why would anyone offer an ordering guarantee except when an ORDER BY clause is included? -
there's an obvious opportunity for parallelism (if sufficient resources are available) to compute each result set in parallel and serve each result row
(from the parallel queries) to the client in whatever order each individual result row becomes available. –
Conor Cunningham:
If you need order in your query results, put in an ORDER BY. It's that simple. Anything else is like riding in a car without a seatbelt.
ALL COMMENTS AND EDIT ARE WELCOME

SQL ALL IN clause

I have been searching for this, but didn't find anything special.
Is it possible to have an SQL query which will act like ALL IN? To better explain, Here is a table structure.
Orders table
OrderItem table (having several columns, but mainly ProductID, OrderID)
ProductGroup table (several columns, but mainly GroupID and ProductID)
I want to write a query which will select all those order which belongs to a specific ProductGroup. So if I have a group named "XYZ" with ID = 10. It has One ProductID in it. Say ProductID01
An order came in with two order items. ProductID01 and ProductID02. To find all orders in the specific Product Group I can use a simple SQL like
SELECT bvc_OrderItem.ProductID, bvc_OrderItem.OrderID
From bvc_OrderItem
INNER JOIN bvc_Product_Group_Product with (nolock) ON bvc_OrderItem.ProductID = bvc_Product_Group_Product.ProductID
WHERE bvc_Product_Group_Product.GroupID = 10
Or I can write using an IN Clause
SELECT bvc_OrderItem.ProductID, bvc_OrderItem.OrderID
From bvc_OrderItem
WHERE ProductID IN (
SELECT ProductID FROM bvc_Product_Group_Product WHERE GroupID=10
)
However, This will return all orders where one or more ProductIDs are part of the product group. I need to return the order row ONLY if ALL of the order items are part of the Product Group
So basically, I need an IN Clause which will considered matched if ALL of the values inside IN Clause matches the rows in bvc_OrderItem.
Or if we are using the Join, then the Join should only succeed if ALL rows on the left have values in the corresponding right table.
If I could write it more simply, I would write it like this
Select ID FROM Table WHERE ID IN (1, 2, 3, 4)
and if the table contains all rows with ids 1,2,3,4; it should return success. If any of these IN values is missing, it should return false and nothing should be selected.
Do you think it is possible? Or there is a workaround to do that?
You can get the list of orders in a variety of ways, such as:
SELECT oi.OrderID
FROM bvc_OrderItem oi JOIN
bvc_Product_Group_Product pgp
ON oi.ProductID = pgp.ProductId AND
pgp.GroupID = 10
GROUP BY oi.OrderID
HAVING COUNT(DISTINCT oi.ProductID) = (SELECT COUNT(*)
FROM bvc_Product_Group_Product
WHERE GroupID = 10
);
Getting the specific products requires an additional join. In most cases, the list of orders is more useful.
The problem with your ALL IN syntax is that it doesn't do what you want. You want to select orders. The syntax:
SELECT bvc_OrderItem.ProductID, bvc_OrderItem.OrderID
From bvc_OrderItem
WHERE ProductID ALL IN (SELECT ProductID
FROM bvc_Product_Group_Product
WHERE GroupID = 10
)
This doesn't specify that you intend for the grouping to be by OrderId, as opposed to some other level.
More fundamentally, though, the SQL language is inspired by relational algebra. The constructs of SELECT, JOIN, WHERE, and GROUP BY directly relate to relational algebra fundamental constructs. The notion of ALL IN -- although sometimes useful -- can be expressed using the more basic building blocks.
You can do it by this tricky statement:
DECLARE #Items TABLE
(
OrderID INT ,
ProductID INT
)
DECLARE #Groups TABLE
(
ProductID INT ,
GroupID INT
)
INSERT INTO #Items
VALUES ( 1, 1 ),
( 1, 2 ),
( 2, 1 ),
( 3, 3 ),
( 3, 4 )
INSERT INTO #Groups
VALUES ( 1, 10 ),
( 2, 10 ),
( 3, 10 ),
( 4, 15 )
SELECT OrderID
FROM #Items i
GROUP BY OrderID
HAVING ( CASE WHEN 10 = ALL ( SELECT gg.GroupID
FROM #Items ii
JOIN #Groups gg ON gg.ProductID = ii.ProductID
WHERE ii.OrderID = i.OrderID ) THEN 1
ELSE 0
END ) = 1
Output:
OrderID
1
2
Also(this is better):
SELECT OrderID
FROM #Items i
JOIN #Groups g ON g.ProductID = i.ProductID
GROUP BY OrderID
HAVING MIN(g.GroupID) = 10
AND MAX(g.GroupID) = 10

How to select info from row above?

I want to add a column to my table that is like the following:
This is just an example of how the table is structured, the real table is more than 10.000 rows.
No_ Name Account_Type Subgroup (New_Column)
100 Sales 3
200 Underwear 0 250 *100
300 Bikes 0 250 *100
400 Profit 3
500 Cash 0 450 *400
So for every time there is a value in 'Subgroup' I want the (New_Column) to get the value [No_] from the row above
No_ Name Account_Type Subgroup (New_Column)
100 Sales 3
150 TotalSales 3
200 Underwear 0 250 *150
300 Bikes 0 250 *150
400 Profit 3
500 Cash 0 450 *400
There are cases where the table is like the above, where two "Headers" are above. And in that case I also want the first above row (150) in this case.
Is this a case for a cursor or what do you recommend?
The data is ordered by No_
--EDIT--
Starting from the first line and then running through the whole table:
Is there a way I can store the value for [No_] where [Subgroup] is ''?
And following that insert this [No_] value in the (New_Column) in each row below having value in the [Subgroup] row.
And when the [Subgroup] row is empty the process will keep going, inserting the next [No_] value in (New_Column), that is if the next line has a value in [Subgroup]
Here is a better image for what I´m trying to do:
SQL Server 2012 suggests using Window Offset Functions.
In this case : LAG
Something like this:
SELECT [No_]
,[Name]
,[Account_Type]
,[Subgroup]
,LAG([No_]) OVER(PARTITION BY [Subgroup]
ORDER BY [No_]) as [PrevValue]
FROM table
Here is an example from MS:
http://technet.microsoft.com/en-us/library/hh231256.aspx
The ROW_NUMBER function will allow you to find out what number the row is, but because it is a windowed function, you will have to use a common table expression (CTE) to join the table with itself.
WITH cte AS
(
SELECT [No_], Name, Account_Type, Subgroup, [Row] = ROW_NUMBER() OVER (ORDER BY [No_])
FROM table
)
SELECT t1.*, t2.[No_]
FROM cte t1
LEFT JOIN cte t2 ON t1.Row = t2.Row - 1
Hope this helps.
Next query will return Name of the parent row instead of the row itself, i.e. Sales for both Sales, Underwear, Bikes; and Profit for Profit, Cash:
select ISNULL(t2.Name, t1.Name)
from table t1
left join table t2 on t1.NewColumn = t2.No
So in SQL Server 2008 i created test table with 3 values in it:
create table #ttable
(
id int primary key identity,
number int,
number_prev int
)
Go
Insert Into #ttable (number)
Output inserted.id
Values (10), (20), (30);
Insert in table, that does what you need (at least if understood correctly) looks like this:
declare #new_value int;
set #new_value = 13; -- NEW value
Insert Into #ttable (number, number_prev)
Values (#new_value,
(Select Max(number) From #ttable t Where t.number < #new_value))
[This part added] And to work with subgroup- just modify the inner select to filter out it:
Select Max(number) From #ttable t
Where t.number < #new_value And Subgroup != #Subgroup
SELECT
No_
, Name
, Account_Type
, Subgroup
, ( SELECT MAX(above.No_)
FROM TableX AS above
WHERE above.No_ < a.No_
AND above.Account_Type = 3
AND a.Account_Type <> 3
) AS NewColumn
FROM
TableX AS a