how can I display same column twice in a table but with different data using sql?
I have 3 tables tbstu, tbexm, tbscr
tbstu = stucod int PK, stunam var(100)
tbexm = exmcod int PK, exmnam var(100)
tbscr = scrcod int PK, scrsub var(100), scrach int
scrstucod int FK (to table tbstu)
screxmcod int FK (to table tbexm)
qry =
select scrcod, screxmcod, scrstucod, scrsub, scrach, scrach
from tbscr, tbstu, tbexm
where scrstucod = stucod
and screxmcod = exmcod
and stucod = 101
and exmcod = 1001
now I want the second scrach in select to display data with respect to exmcod=1002 and Total columns to sum scrach and scrach
If i understood correctly,
Use Left Join:
select
tbscr1.scrcod,
tbscr2.scrcod ,
tbscr1.screxmcod,
tbscr2.screxmcod,
tbscr1.scrstucod
tbscr2.scrstucod,
tbscr1.scrsub,
tbscr2.scrsub
tbscr1.scrach,
tbscr2.scrach,
tbscr1.scrach+tbscr2.scrach AS Total
from tbstu,tbexm
left join
tbscr as tbscr1 on tbscr1.scrstucod =tbstu.stucod and tbscr1.screxmcod = tbexm.exmcod and tbstu.stucod = 101 and tbexm.exmcod =1001
left join
tbscr as tbscr2 on tbscr2 .scrstucod =tbstu.stucod and tbscr2 .screxmcod = tbexm.exmcod and tbstu.stucod = 101 and tbexm.exmcod =1002
you can also try:
select scrcod ,
screxmcod,
scrstucod,
scrsub,
scrach,
(select scrach from tbscr ,tbstu,tbexm
where scrstucod =stucod and screxmcod = exmcod and stucod = 101 and exmcod =1002) AS scrach2
from tbscr ,tbstu,tbexm
where scrstucod =stucod and screxmcod = exmcod and stucod = 101 and exmcod =1001
Based on what I understood, I think this you want to get the following for a given student:
exam information for two exams (1001 and 1002)
total score of the two exams
If that's correct, you can do something like this:
SELECT
sc1.scrcod, sc1.screxmcod, sc1.scrsub, sc1.scrach,
sc2.scrcod, sc2.screxmcod, sc2.scrsub, sc2.scrach,
COALESCE(sc1.scrach, 0) + COALESCE(sc2.scrach, 0) AS TotalScore
FROM tbstu st
LEFT JOIN tbscr sc1 ON st.stucod = sc1.scrstucod AND sc1.screxmcod = 1001 -- Exam 1 info
LEFT JOIN tbscr sc2 ON st.stucod = sc2.scrstucod AND sc2.screxmcod = 1002 -- Exam 2 info
WHERE st.stucod = 101 -- Only get info for student 101
Update
Based on your comments it looks like you want to also split the exam scores based on subject (scrsub). You can just add this field to your JOIN condition:
SELECT
st.stucod,
sc1.scrsub,
sc1.scrach,
sc2.scrach,
COALESCE(sc1.scrach,0) + COALESCE(sc2.scrach,0) AS TotalScore
FROM tbstu st
LEFT JOIN tbscr sc1 -- Exam 1 info
ON st.stucod = sc1.scrstucod AND sc1.screxmcod = 1001
LEFT JOIN tbscr sc2 -- Exam 2 info
ON st.stucod = sc2.scrstucod AND sc2.screxmcod = 1002 AND sc1.scrsub = sc2.scrsub
WHERE st.stucod = 101 -- Only get info for student 101
As coded, this query makes a few assumptions:
this will only work for exams 1001 and 1002
exam 1001 and 1002 have identical scrsub entries (i.e. 4 rows for each exam)
Let me know how that one works.
Related
Have not come across an exact use-case for what I'm trying to achieve on Stackoverflow so will explain it in the hopes someone can assist.
I have two tables, one contains a master activity list and the other contains who completed those activities.
Table A is the Activity Table. This is a distinct list of things that can be done.
ID | Activity
---------------------------
1 | Change Oil
2 | Change Airfilter
3 | Change Brake Fluid
Table B is the Activity Log table. This tracks where people have done one of the above Activities. ActivityID links to ID on Table A.
ID | ActivityID | CompletedBy
---------------------------------------
1 | 1 | john#auto.com
2 | 1 | sally#auto.com
3 | 3 | john#auto.com
What I am trying to produce is a list of all activities, but then only for a distinct person. I have tried multiple ways for this, but can only get the query to show where values exist in both tables.
My preferred output would be the following, where in the query i have asked to show me the full Activity list and also to show where John has completed anything. If there is no record in Table B for this activity, to show a blank value.
ID | Activity | CompletedBy
-------------------------------------------
1 | Change Oil | john#auto.com
2 | Change Airfilter |
3 | Change Brake Fluid | john#auto.com
Here is my current SQL Query in my attempt to work this one out, which right now the results only return ID 1 and 3 from that example above, where john actually has a record in Table B.
Select a.ID, a.Activity, b.CompletedBy
FROM ActivityList a
LEFT OUTER JOIN ActivityLog b
ON a.ID = b.ActivityID
Where CompletedBy = 'john#auto.com'
GROUP BY a.ID, a.Activity, b.CompletedBy
Any help or pointers in the right direction would be greatly appreciated.
If you move the CompletedBy restriction into the join conditions you would not need the group by.
Select a.ID, a.Activity, b.CompletedBy
FROM ActivityList a
LEFT OUTER JOIN ActivityLog b
ON a.ID = b.ActivityID and b.CompletedBy = 'john#auto.com'
Change your where clause to Where CompletedBy = 'john#auto.com' or CompletedBy is null so it matches on a text match or the case where the name does not exist at all. You can also use isnull( to convert the null value to a empty string if that is the behavior you are wanting.
Select a.ID, a.Activity, isnull(b.CompletedBy, '') as CompletedBy
FROM ActivityList a
LEFT OUTER JOIN ActivityLog b
ON a.ID = b.ActivityID
Where CompletedBy = 'john#auto.com' or b.CompletedBy is null
GROUP BY a.ID, a.Activity, b.CompletedBy
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable table_A = new DataTable();
table_A.Columns.Add("ID", typeof(int));
table_A.Columns.Add("Activity", typeof(string));
table_A.Rows.Add(new object[] { 1, "Change Oil"});
table_A.Rows.Add(new object[] { 2, "Change Airfilter"});
table_A.Rows.Add(new object[] { 3, "Change Brake Fluid"});
DataTable table_B = new DataTable();
table_B.Columns.Add("ID", typeof(int));
table_B.Columns.Add("ActivityID", typeof(string));
table_B.Columns.Add("CompletedBy", typeof(string));
table_B.Rows.Add(new object[] { 1, 1, "john#auto.com"});
table_B.Rows.Add(new object[] { 2, 1, "sally#auto.com"});
table_B.Rows.Add(new object[] { 3, 3, "john#auto.com"});
var groups = (from a in table_A.AsEnumerable()
join b in table_B.AsEnumerable() on a.Field<int>("ID") equals b.Field<int>("ID")
select new { a = a, b = b})
.GroupBy(x => x.b.Field<string>("CompletedBy"))
.ToList();
}
}
}
----------
Select a.ID, a.Activity, b.CompletedBy
FROM ActivityList a
Right OUTER JOIN ActivityLog b
ON a.ID = b.ActivityID
Where CompletedBy = 'john#auto.com'
GROUP BY a.ID, a.Activity, b.CompletedBy
Your query is correct just change the potion of condition.
Select a.ID, a.Activity, b.CompletedBy
FROM ActivityList a
LEFT JOIN ActivityLog b
ON a.ID = b.ActivityID AND b.CompletedBy = 'john#auto.com'
GROUP BY a.ID, a.Activity, b.CompletedBy
I am in need of updating a few rows in a database at the same time. Now the issue is that I will only know the FIRST row's unique ID but I will have X number of other rows after that that will need to be updated with some data as well. I will know how many loops I will need in order to update them - just need to know how to go about doing it.
My query looks like this:
UPDATE bLine
SET #val1
WHERE theGuid = #val2;
Now the data for #val1 looks like this:
ID | qty_SerialNum | qty_Location | qty_Property | theGUID
---+---------------+--------------+--------------+---------------------------
5 | 6845fg56 | Home | NA | a45vz-u300-2bd-4710j-vf09
6 | fk469fkh | Dock#4 | NA |
7 | geww2 | Dock#1 | Local |
...
Which when in a string to send over for the #val1:
#val1 = qty_SerialNum = '6845fg56,fk469fkh,geww2',
qty_Location = 'Home,Dock#4,Dock#1',
qty_Property = 'NA,NA,Local'
#val2 = theGUID = 'a45vz-u300-2bd-4710j-vf09'
So that's all fine since I know the GUID of the first row that needs updating but every row after that I wont know.
So the 1st update would look like out of 3:
UPDATE
bLine
SET
(qty_SerialNum = '6845fg56',qty_Location = 'Home',qty_Property = 'NA' )
WHERE
theGuid = 'a45vz-u300-2bd-4710j-vf09';
And this would be the 2nd update out of 3:
UPDATE
bLine
SET
(qty_SerialNum = 'fk469fkh',qty_Location = 'Dock#4',qty_Property = 'NA' )
WHERE
???? = ????;
And finally this would be the 3rd update out of 3:
UPDATE
bLine
SET
(qty_SerialNum = 'geww2',qty_Location = 'Dock#1',qty_Property = 'Local' )
WHERE
???? = ????;
So the question here is - how can I loop through the next X rows and update that information with the 2,3,... values in #val1 (skipping the first since its already saved via the theGUID)?
You can treat this as a gaps and islands type problem where you need to update and island row(row containing guid as well all gap rows till next island).
I am taking some assumption here and below is a working demo
create table bline (ID int, qty_SerialNum varchar(100),qty_Location varchar(100),qty_Property varchar(100), theGUID varchar(100))
insert into bline values
(5,'random','garbage','existing','a45vz-u300-2bd-4710j-vf09')
,(6,'data','random','garbage', NULL)
,(7,'existing','data','.', NULL);
create table #V (qty_SerialNum varchar(100),qty_Location varchar(100),qty_Property varchar(100))
insert into #V values
('6845fg56','Home','NA'),
('fk469fkh','Dock#4','NA'),
('geww2','Dock#1','Local')
;with map as
(
select id,
rn=row_number() over ( order by id asc)
from bline
where theGUID is NOT NULL
),
mappedBline as
(
select
b1.*,
rn
from
bline b1 join
(
select b.id,
rn=max(rn)
from bline b
join map m
on b.id >=m.id
group by b.id
)b2
on b2.id=b1.id
),
updateSet as
(
select
M.*,
updaterow=row_number() over( order by M.id)
from mappedBline M join
mappedBline M2
on M.rn=M2.rn
and M2.theGUID = 'a45vz-u300-2bd-4710j-vf09' --#val2
)
update U
set
qty_SerialNum=V.qty_SerialNum,
qty_Location= V.qty_Location,
qty_Property =V.qty_Property
from
updateSet U join
( select
*, updaterow =row_number() over (order by (select NULL))
from #V
)V
on U.updaterow=V.updaterow
select * from bline
You have a very arcane problem. In general, SQL updates are not for "adjancent" rows in this fashion. However, you can make it work by storing the values in a (virtual) table and joining the two sides together:
with v as (
select v.*
from (values (1, '6845fg56', 'Home', 'NA'),
(2, 'fk469fkh', 'Dock#4', 'NA'),
(3, 'geww2', 'Dock#1', 'Local')
) v(seqnum, qty_SerialNum, qty_Location, qty_Property)
)
update b
set qty_SerialNum = v.qty_SerialNum,
qty_Location = v.qty_Location,
qty_Property = v.qty_Property
from (select top(3) b.*,
row_number() over (order by id) as seqnum
from bline b
where id >= (select id from bline where theGUID = 'a45vz-u300-2bd-4710j-vf09'
order by id
) b join
v
on b.seqnum = v.seqnum;
I want to get the Catalog of a selected supplier along with the last purchase price which is the column Costs in the CommandDetails table as shown bellow :
Product (idproduct, productName)
Command (idCommand, CommandCode, CommandDate, idSupplier)
CommandDetails(idCommandDetails, idProduct, Qty, idCommand, Costs)
Supplier (idSupplier, SupplierName, SupplierAddress)
SupplierCatalog (idSupplier, idProduct)
I tried the row_number() Over (Partition by ...) and other methods but i'm missing something, my brain said enough.
Desired Result :
--------------------------------------------------
| SupplierName| ProductName | CommandDate | Costs|
--------------------------------------------------
| SUP1 | P1 | 01/01/2018 | 3,06 |
| SUP1 | P6 | 01/01/2018 | 1,65 |
| SUP1 | P8 | 03/01/2018 | 5,20 |
| SUP1 | P9 | 05/01/2018 | 8,00 |
| SUP1 | P10 | 01/01/2018 | NULL |
--------------------------------------------------
Null in Costs for P10 when the product has never been ordered.
My last attempt :
SELECT
*
FROM
(SELECT
Sct.idsupplier,
SCt.idProduct,
SCD.PurchasePriceCmd Costs,
SCD.Qty,
P.ProCode,
P.ProName,
Row_number() OVER(PARTITION BY Sct.idProduct order by P.ProCode) rn
FROM SupplierCatalog SCt
LEFT JOIN CommandDetails SCD
ON SCD.idProduct = SCat.idProduct
LEFT JOIN Command a
ON a.idCommand = SCD.idCommand
LEFT OUTER JOIN StoreCommand b
ON a.idCommand = b.idCommand
INNER JOIN Product P
ON P.idProduct = SCt.idProduct) t
where Sct.idSupplier = 4 and rn = 1
You could also try this:
SELECT
s.supplierName,
p.productName,
latestCommandDetail.CommandDate,
latestCommandDetail.Costs
FROM Supplier s
INNER JOIN SupplierCatalog sc ON sc.idSupplier = s.idSupplier
INNER JOIN Product p ON p.idProduct = sc.idProduct
OUTER APPLY
(
SELECT TOP 1
c.CommandDate,
cd.Costs
FROM Command c
INNER JOIN CommandDetails cd ON cd.idCommand = c.idCommand
WHERE c.idSupplier = s.idSupplier AND cd.idProduct = p.idProduct
ORDER BY c.CommandDate DESC
) latestCommandDetail
WHERE s.idSupplier = 4
ORDER BY
s.supplierName,
p.productName
I don't have SQL Server going on this machine at the moment so you may need to tweak the syntax. The trick is just doing a link to a subquery that returns the top row sorted by the date descending.
I'm assuming the idSupplier for "SUP1" is 4 as per you example code above.
OUTER APPLY (the other optional join) will return nulls if no record is returned from the sub query. If you are only interested in products with prices use CROSS APPLY instead.
Also note that this query does not determine what to do if:
- there are two Commands for the same last date from the same supplier for the same product but with different Costs
- the same product is sold twice under the same Command but at different Costs
In both cases this can probably be handled by extending the sort order of the sub-query or by grouping/aggregating the sub-query.
Something like this ought to work:
;with cte as (
select a.idCommand, b.idProduct, row_number() over (partition by b.idProduct
order by a.CommandDate desc) as rn, a.CommandDate, b.Costs
from Command a
inner join CommandDetails b on a.idCommand = b.idCommand
)
select
c.SupplierName, e.productName, cte.CommandDate, cte.Costs
from Supplier c
left join SupplierCatalog d on c.idSupplier = d.idSupplier
left join Product e on d.idProduct = e.idproduct
left join cte on e.idProduct = cte.idproduct and cte.rn = 1
where c.idSupplier = #SupplierNumber;
You can probably replace the common table expression at the top with a subquery, or take some fields out of the CTE and join them in later.
I have a table with "customer_id, date, installment_no, amount" columns. I want to get the information of last installment of each customer_id till today. here installment_no is int type and when a new installment is deposited, the installment_no is increased by 1 in new entry. My table look like:
CS1001 | 12-06-2013 | 1 | 2500
CS1002 | 19-06-2013 | 1 | 1600
CS1001 | 14-07-2013 | 2 | 2500
I want to get a sqlcommand statement for do so.
Group all records by customer_id, then order all customer's records by installment_no, and select only record with max installment_no from each group:
from c in customers
group c by c.customer_id into g
select g.OrderByDescending(x => x.installment_no).First()
Same with pure SQL if you don't use Linq
SELECT c.* FROM Customers c
INNER JOIN (
SELECT customer_id, MAX(installment_no) max_installment
FROM Customers
GROUP BY customer_id
) cmax
ON c.customer_id = cmax.customer_id
AND c.installment_no = cmax.max_installment
var result = list.GroupBy(x=>x.customer_id)
.Select(g=>g.OrderByDescending(y=>y.installment_no).First())
.ToList();
I have the following tables:
*sistema_documentos*
[id], [caminho], [idDocType](FK -> sistema_DocType.id)
*sistema_Indexacao*
[id] ,[idDocumento](FK -> sistema_documentos.id) ,[idIndice](FK ->
sistema_Indexes) ,[valor]
*sistema_DocType*
[id], [tipoNome](FK -> sistema_DocType.id)
*sistema_DocType_Index*
[id],[idName],[mask],[idTipo](FK -> sistema_DocType.id),[tamanho]
From this query:
select distinct a.id, b.idIndice, b.valor from tgpwebged.dbo.sistema_Documentos as a
join tgpwebged.dbo.sistema_Indexacao as b on a.id = b.idDocumento
join tgpwebged.dbo.sistema_DocType as c on a.idDocType = c.id
join tgpwebged.dbo.sistema_DocType_Index as d on c.id = d.docTypeId
where d.docTypeId = 40
and (b.idIndice = 11 AND b.valor = '11111111' OR b.idIndice = 12 AND b.valor = '22222' )
I get the following result
id idIndice valor
13 11 11111111
13 12 22222
14 11 11111111
14 12 22222
16 12 22222
As you can see, I want all ids with idIndice 11 with value 11111111 and 12 with value 22222
Id 16 has id 12 with value 22222 authough it does not have id 11 with value 11111111 so I donĀ“t want it to be shown.
How can I update my query to obtain the result I want. Hope my question is clear. If it is not just ask and I edit my post. Thanks
I would suggest something like this:
WITH TempTable AS
(
select distinct a.id, b.idIndice, b.valor
from tgpwebged.dbo.sistema_Documentos as a
join tgpwebged.dbo.sistema_Indexacao as b on a.id = b.idDocumento
join tgpwebged.dbo.sistema_DocType as c on a.idDocType = c.id
join tgpwebged.dbo.sistema_DocType_Index as d on c.id = d.docTypeId
where d.docTypeId = 40
and (b.idIndice = 11 AND b.valor = '11111111' OR b.idIndice = 12 AND b.valor = '22222' )
)
SELECT *
FROM TempTable t1
WHERE (select count(*)
from TempTable t2
where t1.id = t2.id AND t1.valor != t2.valor) = 1
So... get all the results from your first query where there is at least one result from the table that matches on id, but does not match on valor. (This assumes you could have duplicate rows with the same valor, but you wouldn't want that.)
Try something like this. I took out the tables that didn't have direct bearing on the query, although I named them similarly, and I created a simple schema to replicate the problem. I hope this is clear, and that the connection back to your original query is likewise clear.
CREATE TABLE Documentos (ID INT, document varchar(12))
create table Indexacao (AID INT, indice int, valor varchar(12))
insert Documentos(id, document)
values (1, 'test1'),
(2, 'test2'),
(3, 'test3')
insert Indexacao (aid, indice, valor)
values (1, 11, '11111111'),
(1, 12, '22222'),
(2, 12, '22222')
The important part of the code is the INTERSECT - it returns only rows that are in both sets. In my experience this operator is usually more efficient than anything containing an OR. In the query below, we are getting only those Indexacao rows whose idDocumentos are in the INTERSECT of the two sets of conditions.
SELECT Ind.*
FROM Indexacao Ind
JOIN (
SELECT D.ID
FROM Documentos D
JOIN Indexacao I
ON D.ID = I.AID
WHERE I.Indice = 11 AND I.valor = '11111111'
INTERSECT
SELECT D.ID
FROM Documentos D
JOIN Indexacao I
ON D.ID = I.AID
WHERE I.Indice = 12 AND I.valor = '22222'
)Doc (ID)
ON Doc.ID = Ind.AID
This assumes that you don't have duplicate Indice, Valor rows for a single idDocumento - if you do, you will need to add a DISTINCT.