Related
Table A
id | food_id | price
1 | 3 | 5
2 | 7 | 9
3 | 3 | 8
Table B
id | drink_id | price | type_id
1 | 8 | 8 | 3
2 | 6 | 9 | 3
3 | 6 | 10 | 1
Table C
id(food_id) | Name
3 | Banana
7 | Strawberry
I have 3 tables like this. I want the result of the query written with the stored procedure to be as follows.
column 1
13 (select sum(price) from tableA where food_id = 3)
column 2
2 (Select count(*) from tableB where drink_id = 6)
column 3
9 (Select sum(price) from tableB where drink_id = 6 and type_id = 3)
column 4
Banana (Select Name from tableA a left join tableC c On a.id = c.id) where a.id = 1)
Suppose there are millions of rows of data in these tables. How to write the best stored procedure?
NOTE: Not validated as I don't have your tables, but this format should work once you put it into an SP.
-- put this into an SP
-- delcare varaibles, probably should change them to match results (could be decimal or int depending on what your SUM does)
DECLARE #SumPriceFood AS VARCHAR(50)
DECLARE #CountDrink AS VARCHAR(50)
DECLARE #SumPriceDrink AS VARCHAR(50)
DECLARE #Name AS VARCHAR(50)
-- get your data (not tested these as they are your tables)
SELECT #SumPriceFood = SUM(price) from tableA where food_id = 3
SELECT #CountDrink = COUNT(*) from tableB where drink_id = 6
SELECT #SumPriceDrink = sum(price) from tableB where drink_id = 6 and type_id = 3
SELECT #Name = Name from tableA a left join tableC c On a.id = c.id where a.id = 1
-- now just select the variable values you populated above for your results
SELECT #SumPriceDrink AS SumPriceDrink, #CountDrink AS CountDrink, #SumPriceDrink AS SumPriceDrink, #Name AS Name
you can use your queries as subqueries.
For an Stored procedure it isn't enough but who knows what ypu need it for
DECLARE #food_id INTEGER = 3;
DECLARE #drink_id int = 6;
DECLARE #type_id INTEGER = 3;
DEClARE #a_id int = 1;
SELECT
(select sum(price)from tableA where food_id=#food_id) col1,
(Select count(*) from tableB where drink_id=#drink_id) col2,
(Select sum(price) from tableB where drink_id=#drink_id and type_id=3) col3,
(Select Name from tableA a left join tableC c On a.id = c.id where a.id = #a_id) col4
col1 | col2 | col3 | col4
---: | ---: | ---: | :-----
13 | 2 | 9 | Banana
CREATE PROCEDURE Getdata
#food_id AS INTEGER,#drink_id int,#type_id INTEGER ,#a_id int
AS
SELECT
(select sum(price)from tableA where food_id=#food_id) col1,
(Select count(*) from tableB where drink_id=#drink_id) col2,
(Select sum(price) from tableB where drink_id=#drink_id and type_id=3) col3,
(Select Name from tableA a left join tableC c On a.id = c.id where a.id = #a_id) col4
EXEC Getdata #food_id = 3,#drink_id = 6,#type_id = 3,#a_id = 1;
col1 | col2 | col3 | col4
---: | ---: | ---: | :-----
13 | 2 | 9 | Banana
db<>fiddle here
I have table have four columns like (right ,left ,up ,down) I want to build query that show equaled data on three of the four columns;
example:
| id | right | left | up | down |
|:---|:------:|:-----:|:------:| -----:|
| 1 | street |hospital|coffee |building|
| 2 | house |hospital|coffee |building|
| 3 | road | bus |coffee |sign |
| 4 | house |hospital|coffee |sign |
| 5 | car |road |coffee |sign |
the result should be like:
id
right
left
up
down
1
street
hospital
coffee
building
2
house
hospital
coffee
building
id number 3 and 5 not included because every column should equal it's self
is that query correct
select t.* from test_table t where
(t.right,t.left,t.up) in (select t.right,t.left,t.up from test_table t group by t.right,t.left,t.up having count(*)>1)
or (t.right,t.left,t.down) in (select t.right,t.left,t.down from test_table t group by t.right,t.left,t.down having count(*)>1)
or (t.right,t.up,t.down) in (select t.right,t.up,t.down from test_table t group by t.right,t.up,t.down having count(*)>1)
or (t.left,t.up,t.down) in (select t.left,t.up,t.down from test_table t group by t.left,t.up,t.down having count(*)>1)
and t.mud_id=285 order by t.right,t.left,t.up,t.down ;
if it correct it's go in loop without result for more than 10m waiting
if not
please what is the correct query to get the result
Since there are some assumptions to be made on how how to order and/or organize the results, you may use the query below to identify each matching pair.
DECLARE #T TABLE(ID INT, [right] VARCHAR(20), [left] varchar(20), up
varchar(20), down varchar(20))
INSERT INTO #T VALUES
(1,'street','hospital','coffee','building'),
(2,'house','hospital','coffee','building'),
(3,'road','bus','coffee','sign'),
(4,'house','hospital','coffee','sign'),
(5,'car','road','coffee','sign'),
(6,'street','road','coffee','sign')
SELECT
T1.ID AS MatchID1,
T2.ID AS MatchID2
FROM
#T T1
INNER JOIN #T T2 ON T1.ID <> T2.ID
GROUP BY
T1.ID, T2.ID
HAVING
MAX(CASE WHEN T1.[right] = T2.[right] THEN 1 ELSE 0 END +
CASE WHEN T1.[left] = T2.[left] THEN 1 ELSE 0 END +
CASE WHEN T1.up = T2.up THEN 1 ELSE 0 END +
CASE WHEN T1.down = T2.down THEN 1 ELSE 0 END) = 3
MatchID1 MatchID2
2 1
1 2
4 2
2 4
6 5
5 6
A trick to bring back matches by first occurrence using:
INNER JOIN #T T2 ON T1.ID > T2.ID
MatchID1 MatchID2
1 2
2 4
5 6
I have 2 tables like this
[Table 1]:
|cust_id| tran |item |
| ------| -----|-------
| id1 | 123 |a,b,c |
| id2 | 234 |b,b |
| id3 | 345 |c,d,a,b|
[Table 2]:
| item. | value |
| ----- | ----- |
| a | 1 |
| b | 2 |
| c | 3 |
| d | 4 |
I want to create a target value by doing a lookup from table 2 in table 1 using big query.
|cust_id| tran.|item |target|
| ------| -----|------|------|
| id1 | 123 |a,b,c | 6
| id2 | 234 |b,b | 4
| id3 | 345 |c,d,a,b| 10
What can I try next?
Consider below simple approach
select *,
( select sum(value)
from unnest(split(item)) item
join table2
using (item)
) target
from table1
if applied to sample data in your question - output is
Try the following:
select t1.cust_id
, t1.tran
, t1.item
, sum(t2.value) as target
from table_1 t1
, UNNEST(split(t1.item ,',')) as item_unnested
LEFT JOIN table_2 t2
on item_unnested=t2.item
group by t1.cust_id
, t1.tran
, t1.item
With your data it gives the following:
Create a center table that splits the item column values on rows and join that table with table2.
Try following
--Cursor is used to split the item data row by row
--#temp is a temporary table
create table #temp (id varchar(10), trans varchar(10), item varchar(10), item1 varchar(10));
DECLARE #item varchar(10);
DECLARE #id varchar(10);
DECLARE #trans varchar(10);
DECLARE item_cusor CURSOR FOR
SELECT *
FROM table1;
OPEN item_cusor
FETCH NEXT FROM item_cusor
INTO #id,#trans,#item
WHILE ##FETCH_STATUS = 0
BEGIN
insert into #temp
SELECT #id,#trans,#item,*
FROM STRING_SPLIT (#item, ',')
FETCH NEXT FROM item_cusor
INTO #id,#trans,#item
END
CLOSE item_cusor;
DEALLOCATE item_cusor;
--select * from temp
select t.id as cust_id, t.trans,t.item , sum(cast(t2.value as int)) as target
from #temp t
JOIN table2 t2
on t.item1=t2.item
group by t.id, t.trans,t.item;
Cursors: https://www.c-sharpcorner.com/article/cursors-in-sql-server/
Temporary tables: https://www.sqlservertutorial.net/sql-server-basics/sql-server-temporary-tables/
String split function: https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql
I have two tables in SQL Server, the first one for inventory and second for inventory movement. I need a query to show the remaining raw material for each serial number and if there's a movement or not.
Table 1
+----------+------------+-----+
| CodeRaw | Serial_Raw | Qty |
+----------+------------+-----+
| 1 | 1 | 100 |
| 1 | 2 | 150 |
| 2 | 1 | 80 |
| 1 | 3 | 100 |
| 1 | 4 | 100 |
+----------+------------+-----+
And Table 2
+------------+----------+------------+----------+--+
| CodeBatch | CodeRaw | Serial_Raw | Qty_Added| |
+------------+----------+------------+----------+--+
| 1 | 1 | 1 | 80 | |
| 2 | 1 | 1 | 10 | |
| 3 | 1 | 2 | 150 | |
| 4 | 1 | 3 | 80 | |
+------------+----------+------------+----------+--+
I've already tried some code but I just got results where there is an inventory movenent, not showing all QT for a specific raw (Code_Raw).
Probably I'm missing something....
This is the query I have
declare #tbl1 table (CodeRaw INT, Serial_Raw INT, Qty INT)
declare #tbl2 table (CodeBatch INT, CodeRaw INT, Serial_Raw INT, QtyAdded INT)
insert into #tbl1 values (1,1,100), (1,2,150), (2,1,80), (1,3,100),(1,4,100)
insert into #tbl2 values (1,1,1,80), (2,1,1,10), (3,1,2,150), (4,1,3,80)
SELECT t2.Serial_Raw, t1.Qty - t2.QtyAdded AS Total_Remaining FROM #tbl1 t1
INNER JOIN (SELECT CodeRaw, Serial_Raw , SUM(QtyAdded) QtyAdded FROM #tbl2
GROUP BY CodeRaw, Serial_Raw) AS t2 ON t2.CodeRaw = t1.CodeRaw AND t1.Serial_Raw = t2.Serial_Raw
WHERE t1.CodeRaw = 1
I expected
Serial_Raw Total_Remaining
---------- ---------------
1 10
2 0
3 20
4 100
But the result is
Serial_Raw Total_Remaining
---------- ---------------
1 10
2 0
3 20
Thanks in advance
I think your issue is your INNER JOIN. It is only returning results for your inventory that's in both tables. Meaning, if an item has no inventory movement, the total count is not returned.
Step 1: Switch your INNER JOIN to a LEFT OUTER JOIN. This will return all results in the inventory table (tbl1) even if it has no movement (tbl2). Also, select Serial_Raw from tbl1 instead of tbl2 in case of NULL value returned from JOIN.
Step 2: Step 1 will return NULL for the tbl2.QtyAdded in your JOIN. To account for this, you can do a NULL check in your calculation by using ISNULL(tbl2.QtyAdded, 0). Then, if there is not QtyAdded, the tbl1.Qty will subtract 0 (stay the same).
Resulting Code:
declare #tbl1 table (CodeRaw INT, Serial_Raw INT, Qty INT)
declare #tbl2 table (CodeBatch INT, CodeRaw INT, Serial_Raw INT, QtyAdded INT)
insert into #tbl1 values (1,1,100), (1,2,150), (2,1,80), (1,3,100),(1,4,100)
insert into #tbl2 values (1,1,1,80), (2,1,1,10), (3,1,2,150), (4,1,3,80)
SELECT t1.Serial_Raw, t1.Qty - ISNULL(t2.QtyAdded, 0) AS Total_Remaining FROM #tbl1 t1
LEFT OUTER JOIN (SELECT CodeRaw, Serial_Raw , SUM(QtyAdded) QtyAdded FROM #tbl2
GROUP BY CodeRaw, Serial_Raw) AS t2 ON t2.CodeRaw = t1.CodeRaw
AND t1.Serial_Raw = t2.Serial_Raw
WHERE t1.CodeRaw = 1
Results:
Serial_Raw Total_Remaining
-----------------------------
1 10
2 0
3 20
4 100
Use LEFT OUTER JOIN instead INNER JOIN, also ISNULL and serial_Raw from left table.
declare #tbl1 table (CodeRaw INT, Serial_Raw INT, Qty INT)
declare #tbl2 table (CodeBatch INT, CodeRaw INT, Serial_Raw INT, QtyAdded INT)
insert into #tbl1 values (1,1,100), (1,2,150), (2,1,80), (1,3,100),(1,4,100)
insert into #tbl2 values (1,1,1,80), (2,1,1,10), (3,1,2,150), (4,1,3,80)
SELECT t1.Serial_Raw, t1.Qty - ISNULL(t2.QtyAdded,0) AS Total_Remaining FROM #tbl1 t1
LEFT OUTER JOIN (SELECT CodeRaw, Serial_Raw , SUM(QtyAdded) QtyAdded FROM #tbl2
GROUP BY CodeRaw, Serial_Raw) AS t2
ON t2.CodeRaw = t1.CodeRaw AND t1.Serial_Raw = t2.Serial_Raw
WHERE t1.CodeRaw = 1
Result:
Serial_Raw Total_Remaining
----------- ---------------
1 10
2 0
3 20
4 100
You need to do 2 changes
Left join to the second table.
Check the second table's column is null, then set to 0 for minus
SELECT t1.Serial_Raw, t1.Qty - isnull(t2.QtyAdded, 0) AS Total_Remaining
FROM #tbl1 t1
Left Join
(SELECT CodeRaw, Serial_Raw, SUM(QtyAdded) QtyAdded FROM #tbl2 GROUP BY CodeRaw, Serial_Raw)
AS t2 ON t2.CodeRaw = t1.CodeRaw AND t1.Serial_Raw = t2.Serial_Raw
Where t1.CodeRaw = 1
I am looking for a way to pick the highest and lowest value (integer) from a single row in table. There are 4 columns that i need to compare together and get highest and lowest number there is.
The table looks something like this...
id | name | col_to_compare1 | col_to_compare2 | col_to_compare3 | col_to_compare4
1 | John | 5 | 5 | 2 | 1
2 | Peter | 3 | 2 | 4 | 1
3 | Josh | 3 | 5 | 1 | 3
Can you help me, please? Thanks!
You can do this using CROSS APPLY and the VALUES clause. Use VALUES to group all your compared columns and then select the max.
SELECT
MAX(d.data1) as MaxOfColumns
,MIN(d.data1) as MinOfColumns
,a.id
,a.name
FROM YOURTABLE as a
CROSS APPLY (
VALUES(a.col_to_compare1)
,(a.col_to_compare2)
,(a. col_to_compare3)
,(a.col_to_compare4)
,(a. col_to_compare5)
) as d(data1) --Name the Column
GROUP BY a.id
,a.name
Assuming you are looking for min/max per row
Declare #YourTable table (id int,name varchar(50),col_to_compare1 int,col_to_compare2 int,col_to_compare3 int,col_to_compare4 int)
Insert Into #YourTable values
(1,'John',5,5,2,1),
(2,'Peter',3,2,4,1),
(3,'Josh',3,5,1,3)
Select A.ID
,A.Name
,MinVal = min(B.N)
,MaxVal = max(B.N)
From #YourTable A
Cross Apply (Select N From (values(a.col_to_compare1),(a.col_to_compare2),(a.col_to_compare3),(a.col_to_compare4)) N(N) ) B
Group By A.ID,A.Name
Returns
ID Name MinVal MaxVal
1 John 1 5
3 Josh 1 5
2 Peter 1 4
These solutions keep the current rows and add additional columns of min/max.
select *
from t cross apply
(select min(col) as min_col
,max(col) as max_col
from (
values
(t.col_to_compare1)
,(t.col_to_compare2)
,(t.col_to_compare3)
,(t.col_to_compare4)
) c(col)
) c
OR
select *
,cast ('' as xml).value ('min ((sql:column("t.col_to_compare1"),sql:column("t.col_to_compare2"),sql:column("t.col_to_compare3"),sql:column("t.col_to_compare4")))','int') as min_col
,cast ('' as xml).value ('max ((sql:column("t.col_to_compare1"),sql:column("t.col_to_compare2"),sql:column("t.col_to_compare3"),sql:column("t.col_to_compare4")))','int') as max_col
from t
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| id | name | col_to_compare1 | col_to_compare2 | col_to_compare3 | col_to_compare4 | min_col | max_col |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 1 | John | 5 | 5 | 2 | 1 | 1 | 5 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 2 | Peter | 3 | 2 | 4 | 1 | 1 | 4 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 3 | Josh | 3 | 5 | 1 | 3 | 1 | 5 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
A way to do this is to "break" apart the data
declare #table table (id int, name varchar(10), col1 int, col2 int, col3 int, col4 int)
insert into #table values (1 , 'John' , 5 , 5 , 2 , 1)
insert into #table values (2 , 'Peter' , 3 , 2 , 4 , 1)
insert into #table values (3 , 'Josh' , 3 , 5 , 1 , 3)
;with stretch as
(
select id, col1 as col from #table
union all
select id, col2 as col from #table
union all
select id, col3 as col from #table
union all
select id, col4 as col from #table
)
select
t.id,
t.name,
agg.MinCol,
agg.MaxCol
from #table t
inner join
(
select
id, min(col) as MinCol, max(col) as MaxCol
from stretch
group by id
) agg
on t.id = agg.id
Seems simple enough
SELECT min(col1), max(col1), min(col2), max(col2), min(col3), max(col3), min(col4), max(col4) FROM table
Gives you the Min and Max for each column.
Following OP's comment, I believe he may be looking for a min/max grouped by the person being queried against.
So that would be:
SELECT name, min(col1), max(col1), min(col2), max(col2), min(col3), max(col3), min(col4), max(col4) FROM table GROUP BY name