SQL Remove Duplicate header when doing inner join - sql

I'm trying to Remove the Duplicate Header of my Query...
here's my query
Select po.BranchOrderNumber,pod.ItemCode, pod.ItemDescription From RetailPosOrders po
INNER JOIN RetailPosOrderDetails pod
ON po.BranchOrderID = pod.OrderID
my query's result now look's like this.
now what I want is something like this.
thanks in advance. I was planning to use this on Report builder.. I am using Microsoft Sql Server 2014.

This is the type of transformation that is best done at the application level. It is possible in SQL, but do recall that SQL queries and result sets -- by default -- are unordered. Your desired results have an ordering.
But, you can do this using row_number():
Select (case when row_number() over (partition by po.BranchOrderNumber order by pod.ItemCode) = 1
then po.BranchOrderNumber else ''
end) as BranchOrderNumber
pod.ItemCode, pod.ItemDescription
From RetailPosOrders po INNER JOIN
RetailPosOrderDetails pod
ON po.BranchOrderID = pod.OrderID
Order by po.BranchOrderNumber, pod.ItemCode;
This assumes that po.BranchOrderNumber -- despite its name -- is stored as a string (the leading zeroes suggest that this is the case).
Also, a couple of important things:
The outer order by needs to be the same as the fields used in the over clause.
The fields need to uniquely define each row in the result set. The order by in SQL is not stable, meaning that keys with the same value can appear in any order, even for different runs of the same query.

There is no way to do this in Mssql server itself.
I think you have to do it in the application level if you are going to display it using console/ win / web application.

Related

Having troubles with a conditional count in SQL

I'm working on an SQL project (involving a library database) and I'm having a hard time figuring out how to make a conditional count.
So, I have 4 tables: Imprumuturi, Cititori, Autori, Carti. I need to list the 'Cititori' that have more than one borrowed 'Carti' at the current time.
I tried to use
SELECT cititori.nume_cititor,COUNT(imprumuturi.pk_cititor)
AS numar_imprumuturi FROM cititori, imprumuturi
WHERE imprumuturi.data_return IS NULL GROUP BY cititori.nume_cititor
HAVING COUNT(imprumuturi.pk_cititor)>1
ORDER BY cititori.nume_cititor;
And while it lists all the 'Cititori", it doesn't count the number of active borrowed 'Carti' as it should.
Can I get a hint or some help on how to make it work?
These are the fields of my database
Seems you missed the relation between the tables:
SELECT cititori.nume_cititor,COUNT(imprumuturi.pk_cititor)
AS numar_imprumuturi
FROM cititori
INNER JOIN imprumuturi ON imprumuturi.pk_cititori = cititori.pk_cititori
WHERE imprumuturi.data_return IS NULL
GROUP BY cititori.nume_cititor
HAVING COUNT(imprumuturi.pk_cititor)>1
ORDER BY cititori.nume_cititor;
As suggested, you should not use the old implicit join syntax based on comma-separated table names and where condition, but use explicit join syntax.

SQL Server: Using columns with identical names

I'm writing a migration script to move data from one data model to another in Microsoft SQL Server Management Studio. The problem I'm running into is that, in the source database, some tables have foreign key columns that I need to compare. A snippet of code:
INSERT INTO TargetDB.dbo.Encounter(EncounterID, PATID, DRG)
Select
visit_occurrence_id,
person_id,
(Select
Case when ((Select top 1 observation_concept_id from SourceDB.dbo.Observation where visit_occurrence_id = visit_occurrence_id) = 3040464)
Then (Select top 1 value_as_string from SourceDB.dbo.Observation where visit_occurrence_id = visit_occurrence_id)
Else NULL End
)
from SourceDB.dbo.Visit_occurrence
As you can see, I need to compare visit_occurrence_id in SourceDB.dbo.Observation to visit_occurrence_id in SourceDB.dbo.Visit_occurrence. As it is, it's just returning values from the first row in SourceDB.dbo.Observation, since visit_occurrence_id will always equal itself.
What's the proper way to do this? Can I assign the first visit_occurrence_id value to a variable within the query, so it has a distinct name? I'm pretty lost here.
I'm going to add a little more detail for you here in an answer. You can always refer to an object by it's fully-qualified name, but it isn't always necessary:
Database.Schema.Table
or
Database.Schema.Table.Column
with sql server, it can even include server for linked-server scenarios.
also true of other objects like views, procedures, functions, etc... Aliasing of tables and/or columns can be a good strategy for shortening this qualification.
Anytime there is ambiguity, this is necessary. However, it is a good practice to be fairly explicit, because it can save you future headaches. As an example, consider this view:
CREATE VIEW vwEmployeesWithLocation AS
SELECT
E.EmployeeId -- from employees
, LastName -- from employees
, Status -- from employees
, LocationName -- from locations
FROM
Employees AS E
INNER JOIN
EmployeeLocations AS EL ON E.EmloyeeId = EL.EmployeeId
INNER JOIN
Locations AS L ON EL.LocationId = L.LocationId
Right now, everything is fine because other than EmployeeId, the column names are distinct. However, someone might add a Status column to the Locations table in the future and break this view. So, it would be better to explicitly include the table prefix for all columns in the select.
In your case, your query is cross database, so again, be explicit about the database in all parts of your query.
Used snow_FFFFFF's answer in the comments: Just used SourceDB.dbo.Observation.visit_occurence_id.

Filtering on ROW_NUMBER() is changing the results

I did implement an OData service of my own that takes an SQL statement and apply the top / skip filter using a ROW_NUMBER(). Most statement tested so far are working well except for a statement involving 2 levels of Left Join. For some reason I can't explain, the data returned by the sql is changing when I apply a where clause on the row number column.
For readability (and testing), I removed most of the sql to keep only the faulty part. Basically, you have a Patients table that may have 0 to N Diagnostics and the Diagnostics may have 0 to N Treatments:
SELECT RowNumber, PatientID, DiagnosticID, TreatmentID
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS RowNumber
, *
FROM PATIENTS
LEFT JOIN DIAGNOSTICS ON DIAGNOSTICS.PatientID = PATIENTS.PatientID
LEFT JOIN TREATMENTS ON TREATMENTS.DiagnosticID = DIAGNOSTICS.DiagnosticID
) AS Wrapper
--WHERE RowNumber BETWEEN 1 AND 10
--If I uncomment the line above, I'll get 10 lines that differs from the first 10 line of this query
This is the results I got from the statement above. The result on the left is showing the first 10 rows without the WHERE clause while the one on the right is showing the results with the WHERE clause.
For the record, I'm using SQL Server 2008 R2 SP3. My application is in C# but the problem occurs in SQL server too so I don't think .NET is involved in this case.
EDIT
About the ORDER BY (SELECT NULL), I took that code a while ago from this SO question. However, an order by null will work only if the statement is sorted... in my case, I forgot about adding an order by clause so that's why I was getting some random sorting.
Let me first ask: why do you expect it to be the same? Or rather, why do you expect it to be anything in particular? You haven't imposed an ordering, so the query optimizer is free to use whatever execution operators are most efficient (according to its cost scheme). When you add the WHERE clause, the plan will change and the natural ordering of the results will be different. This can also happen when adding joins or subqueries, for example.
If you want the results to come back in a specific order, you need to actually use the ORDER BY subclause of the ROW_NUMBER() window function. I'm not sure why you are ordering by SELECT NULL, but I can guarantee you that's the problem.

In an EXISTS can my JOIN ON use a value from the original select

I have an order system. Users with can be attached to different orders as a type of different user. They can download documents associated with an order. Documents are only given to certain types of users on the order. I'm having trouble writing the query to check a user's permission to view a document and select the info about the document.
I have the following tables and (applicable) fields:
Docs: DocNo, FileNo
DocAccess: DocNo, UserTypeWithAccess
FileUsers: FileNo, UserType, UserNo
I have the following query:
SELECT Docs.*
FROM Docs
WHERE DocNo = 1000
AND EXISTS (
SELECT * FROM DocAccess
LEFT JOIN FileUsers
ON FileUsers.UserType = DocAccess.UserTypeWithAccess
AND FileUsers.FileNo = Docs.FileNo /* Errors here */
WHERE DocAccess.UserNo = 2000 )
The trouble is that in the Exists Select, it does not recognize Docs (at Docs.FileNo) as a valid table. If I move the second on argument to the where clause it works, but I would rather limit the initial join rather than filter them out after the fact.
I can get around this a couple ways, but this seems like it would be best. Anything I'm missing here? Or is it simply not allowed?
I think this is a limitation of your database engine. In most databases, docs would be in scope for the entire subquery -- including both the where and in clauses.
However, you do not need to worry about where you put the particular clause. SQL is a descriptive language, not a procedural language. The purpose of SQL is to describe the output. The SQL engine, parser, and compiler should be choosing the most optimal execution path. Not always true. But, move the condition to the where clause and don't worry about it.
I am not clear why do you need to join with FileUsers at all in your subquery?
What is the purpose and idea of the query (in plain English)?
In any case, if you do need to join with FileUsers then I suggest to use the inner join and move second filter to the WHERE condition. I don't think you can use it in JOIN condition in subquery - at least I've never seen it used this way before. I believe you can only correlate through WHERE clause.
You have to use aliases to get this working:
SELECT
doc.*
FROM
Docs doc
WHERE
doc.DocNo = 1000
AND EXISTS (
SELECT
*
FROM
DocAccess acc
LEFT OUTER JOIN
FileUsers usr
ON
usr.UserType = acc.UserTypeWithAccess
AND usr.FileNo = doc.FileNo
WHERE
acc.UserNo = 2000
)
This also makes it more clear which table each field belongs to (think about using the same table twice or more in the same query with different aliases).
If you would only like to limit the output to one row you can use TOP 1:
SELECT TOP 1
doc.*
FROM
Docs doc
INNER JOIN
FileUsers usr
ON
usr.FileNo = doc.FileNo
INNER JOIN
DocAccess acc
ON
acc.UserTypeWithAccess = usr.UserType
WHERE
doc.DocNo = 1000
AND acc.UserNo = 2000
Of course the second query works a bit different than the first one (both JOINS are INNER). Depeding on your data model you might even leave the TOP 1 out of that query.

SQL- make all rows show a column value if one of the rows has it

I have an SQL statement for a PICK sheet that returns the header/detail records for an order.
One of the fields in the SQL is basically a field to say if there are dangerous goods. If a single product on the order has a code against it, then the report should display that its hazardous.
The problem I am having is that in the SQL results, because I am putting the code on the report in the header section (and not the detail section), it is looking for the code only on the first row.
Is there a way through SQL to basically say "if one of these rows has this code, make all of these rows have this code"? I'm guessing a subselect would work here... the problem is, is that I am using a legacy system built on FoxPro and FoxPro SQL is terrible!
EDIT: just checked and I am running VFP8, subqueries in the SELECT statement were added in FVP9 :(
SELECT Header.HeaderId, Header.HeaderDescription,
Detail.DetailId, Detail.DetailDescription, Detail.Dangerous,
Danger.DangerousItems
FROM Header
INNER JOIN Detail ON Header.HeaderId = Detail.HeaderId
LEFT OUTER JOIN
(SELECT HeaderId, COUNT(*) AS DangerousItems FROM Detail WHERE Dangerous = 1 GROUP BY HeaderId) Danger ON Header.HeaderId = Danger.HeaderId
If Danger.DangerousItems > 0 then something is dangerous. If it is Null then nothing is dangerous.
If you can't do nested queries, then you should be able to create a view-like object (called a query in VFP8) for the nested select:
SELECT HeaderId, COUNT(*) AS DangerousItems FROM Detail WHERE Dangerous = 1 GROUP BY HeaderId
and then can you left join on that?
In VFP 8 and earlier, your best bet is to use three queries in a row:
SELECT Header.HeaderId, Header.HeaderDescription,
Detail.DetailId, Detail.DetailDescription, Detail.Dangerous,
Danger.DangerousItems
FROM Header
INNER JOIN Detail ON Header.HeaderId = Detail.HeaderId
INTO CURSOR csrDetail
SELECT HeaderId, COUNT(*) AS DangerousItems
FROM Detail
WHERE Dangerous
GROUP BY HeaderId
INTO CURSOR csrDanger
SELECT csrDetail.*, csrDanger.DangerousItems
FROM csrDetail.HeaderID = csrDanger.HeaderID
INTO CURSOR csrResult