t-sql - was ist der effizienteste weg, um eine tabelle zu durchlaufen, bis eine bedingung erfüllt ist
<p>ich habe eine programmieraufgabe im bereich <code>t-sql</code>.</p><p><strong> aufgabe: </strong></p><ol> <li>die leute wollen in einen aufzug, jede person hat ein bestimmtes gewicht.</li> <li>die reihenfolge der in der schlange wartenden personen wird durch die spaltenumdrehung bestimmt.</li> <li>der aufzug hat eine maximale kapazität von &lt;= 1000 lbs.</li> <li>geben sie den namen der letzten person zurück, die den aufzug betreten kann, bevor er zu schwer wird!</li> <li>der rückgabetyp sollte table sein</li> </ol><p> <a href="https://i.stack.imgur.com/qpeok.png">(<img src="https://i.stack.imgur.com/qpeok.png" alt="enter image description here"></a> </p><p><strong> frage: </strong> was ist der effizienteste weg, um dieses problem zu lösen? wenn die schleife korrekt ist, gibt es raum für verbesserungen?</p><p><strong> ich habe eine schleife und # temporäre tabellen verwendet, hier meine lösung: </strong></p><pre><code>set rowcount 0 -- the source table "line" has the same schema as #result and #temp use northwind go declare @sum int declare @curr int set @sum = 0 declare @id int if object\_id('tempdb..#temp','u') is not null drop table #temp if object\_id('tempdb..#result','u') is not null drop table #result create table #result( id int not null, [name] varchar(255) not null, weight int not null, turn int not null ) create table #temp( id int not null, [name] varchar(255) not null, weight int not null, turn int not null ) insert into #temp select * from line order by turn while exists (select 1 from #temp) begin -- get the top record select top 1 @curr = r.weight from #temp r order by turn select top 1 @id = r.id from #temp r order by turn --print @curr print @sum if(@sum + @curr &lt;= 1000) begin print 'entering........ again' --print @curr set @sum = @sum + @curr --print @sum insert into #result select * from #temp where [id] = @id --id, [name], turn delete from #temp where id = @id end else begin print 'breaaaking.' break end end select top 1 [name] from #result r order by r.turn desc </code></pre><p>hier das create-skript für die tabelle, die ich mit northwind zum testen verwendet habe:</p><pre><code>use [northwind] go /****** object: table [dbo].[line] script date: 28.05.2018 21:56:18 ******/ set ansi\_nulls on go set quoted\_identifier on go create table [dbo].[line]( [id] [int] not null, [name] [varchar](255) not null, [weight] [int] not null, [turn] [int] not null, primary key clustered ( [id] asc )with (pad\_index = off, statistics\_norecompute = off, ignore\_dup\_key = off, allow\_row\_locks = on, allow\_page\_locks = on) on [primary], unique nonclustered ( [turn] asc )with (pad\_index = off, statistics\_norecompute = off, ignore\_dup\_key = off, allow\_row\_locks = on, allow\_page\_locks = on) on [primary] ) on [primary] go alter table [dbo].[line] with check add check (([weight]&gt;(0))) go insert into [dbo].[line] ([id], [name], [weight], [turn]) values (5, 'gary', 800, 1), (3, 'jo', 350, 2), (6, 'thomas', 400, 3), (2, 'will', 200, 4), (4, 'mark', 175, 5), (1, 'james', 100, 6) ; </code></pre>
<p>sie sollten versuchen, schleifen generell zu vermeiden. sie sind normalerweise weniger effizient als satzbasierte lösungen und weniger lesbar.</p><p>das folgende sollte ziemlich effizient sein.</p><p>dies gilt umso mehr, wenn die spalten name und weight im index <code>include-</code> d sein könnten, um die schlüsselsuche zu vermeiden.</p><p>es kann den eindeutigen index in der reihenfolge <code>turn</code> scannen und die laufende summe der spalte <code>weight</code> berechnen. verwenden sie dann <code>lead</code> mit denselben ordnungskriterien, um die laufende summe zu ermitteln die nächste reihe wird sein.</p><p>sobald die erste zeile gefunden wird, in der diese 1000 überschreitet oder <code>null</code> ist (was anzeigt, dass keine nächste zeile vorhanden ist), kann der scan gestoppt werden.</p><pre><code>with t1 as (select *, sum(weight) over (order by turn rows unbounded preceding) as cume\_weight from [dbo].[line]), t2 as (select lead(cume\_weight) over (order by turn) as next\_cume\_weight, * from t1) select top 1 name from t2 where next\_cume\_weight &gt; 1000 or next\_cume\_weight is null order by turn </code></pre><h1>ausführungsplan</h1><p> <a href="https://i.stack.imgur.com/gb1mw.png">(<img src="https://i.stack.imgur.com/gb1mw.png" alt="enter image description here"></a> </p><p>in der praxis scheint es ein paar zeilen vor dem unbedingt erforderlichen zu lesen - es sieht so aus, als würde jedes fenster-spool/stream-aggregat-paar zwei zusätzliche zeilen lesen.</p><p>für die beispieldaten in der frage müssten im idealfall nur zwei zeilen aus dem index-scan gelesen werden, in wirklichkeit werden jedoch 6 zeilen gelesen. dies ist jedoch kein wesentliches effizienzproblem und verschlechtert sich nicht, wenn der tabelle weitere zeilen hinzugefügt werden (wie in) <a href="https://dbfiddle.uk/?rdbms=sqlserver\_2017&amp;fiddle=892e6bb7c1af912413b01d05e606a98a">diese demo</a> )</p><p>für diejenigen, die an dieser ausgabe interessiert sind, wird unten ein bild mit den von jedem operator ausgegebenen zeilen (wie durch das erweiterte ereignis <code>query\_trace\_column\_values</code> gezeigt) angezeigt. die zeilen werden in der reihenfolge <code>row\_id</code> ausgegeben (beginnend mit <code>47</code> für die erste zeile, die vom index-scan gelesen wurde und bei <code>113</code> für <code>top</code> endet)</p><p>klicken sie auf das bild unten, um es zu vergrößern, oder sehen sie alternativ <a href="https://imgur.com/a/c7npqqh">die animierte version, um den ablauf einfacher zu verfolgen</a> .</p><p>anhalten der animation an dem punkt, an dem das rechte stream-aggregat seine erste zeile ausgegeben hat (für gary - turn = 1). es scheint offensichtlich, dass es darauf gewartet hat, seine erste zeile mit einem anderen windowcount zu erhalten (für jo - turn = 2). und die fensterspule gibt die erste "jo" -zeile erst frei, wenn sie die nächste zeile mit einem anderen <code>turn</code> gelesen hat (für thomas - turn = 3)</p><p>die fensterspule und das stream-aggregat bewirken also, dass eine zusätzliche zeile gelesen wird, und es gibt vier davon im plan - daher 4 zusätzliche zeilen.</p><p> <a href="https://i.stack.imgur.com/cwllc.jpg">(<img src="https://i.stack.imgur.com/cwllc.jpg" alt="enter image description here"></a> </p><p>es folgt eine erläuterung der oben gezeigten spalten (basierend auf info <a href="https://www.microsoft.com/en-us/p/microsoft-sql-server-2012-high-performance-t-sql-using-window-functions/fgqpf3h0qlch">hier</a> )</p><ul> <li><strong> knotenname: index-scan, knoten-id: 15, spaltenname: id </strong> basistabellenspalte, die vom index abgedeckt wird</li> <li><strong> knotenname: index-scan, knoten-id: 15, spaltenname: drehen sie die basistabellenspalte </strong>, die vom index abgedeckt wird</li> <li><strong> nodename: clustered index seek, nodeid: 17, columnname: gewicht </strong> basistabellenspalte, die aus der suche abgerufen wurde</li> <li><strong> nodename: clustered index seek, nodeid: 17, columnname: name </strong> basistabellenspalte, die aus der suche abgerufen wurde</li> <li><strong> nodename: segment, nodeid: 13, columnname: segment1010 </strong> gibt 1 am anfang einer neuen gruppe zurück oder andernfalls null. da kein <code>partition by</code> in <code>sum</code> vorhanden ist, erhält nur die erste zeile 1</li> <li><strong> nodename: sequenzprojekt, nodeid: 12, columnname: rownumber1009 </strong> <code>row\_number()</code> innerhalb der durch das segment1010-flag angegebenen gruppe. da sich alle zeilen in derselben gruppe befinden, werden ganze zahlen von 1 bis 6 aufgestiegen. wird in fällen wie <code>rows between 5 preceding and 2 following</code> zum filtern von zeilen mit rechten rahmen verwendet. (oder wie für <code>lead</code> später)</li> <li><strong> nodename: segment, nodeid: 11, columnname: segment1011 </strong> gibt 1 am anfang einer neuen gruppe zurück oder andernfalls null. da kein <code>partition by</code> in <code>sum</code> vorhanden ist, erhält nur die erste zeile 1 (wie segment1010).</li> <li><strong> nodename: window spool, nodeid: 10, columnname: windowcount1012 </strong> attribut, das zeilen gruppiert, die zu einem fensterrahmen gehören. diese fensterspule verwendet den fall "fast track" für <code>unbounded preceding</code>. wo es zwei zeilen pro quellzeile ausgibt. eins mit den kumulativen werten und eins mit den detailwerten. obwohl es keinen sichtbaren unterschied in den durch <code>query\_trace\_column\_values</code> offenbarten zeilen gibt, gehe ich davon aus, dass kumulative spalten in der realität vorhanden sind.</li> <li><strong> nodename: stream aggregate, nodeid: 9, columnname: expr1004 </strong> <code>count(*)</code> gruppiert nach windowcount1012 gemäß plan, aber tatsächlich eine laufende anzahl</li> <li><strong> nodename: stream aggregate, nodeid: 9, columnname: expr1005 </strong> <code>sum(weight)</code> gruppiert nach windowcount1012 gemäß plan, aber tatsächlich die laufende gewichtssumme (dh <code>cume\_weight</code>)</li> <li><strong> nodename: segment, nodeid: 7, columnname: expr1002 </strong> <code>case when [expr1004]=(0) then null else [expr1005] end</code> - sehen sie nicht, wie <code>count(*)</code> kann sei 0, also läuft immer die summe (<code>cume\_weight</code>)</li> <li><strong> knotenname: segment, knoten-id: 7, spaltenname: segment1013 </strong> nein <code>partition by</code> in <code>lead</code>, sodass die erste zeile 1 erhält. alle verbleibenden werden null</li> <li><strong> nodename: sequenzprojekt, nodeid: 6, columnname: rownumber1006 </strong> <code>row\_number()</code> innerhalb der durch das segment1013-flag angegebenen gruppe. da sich alle zeilen in derselben gruppe befinden, sind dies ganze zahlen von 1 bis 4</li> <li><strong> nodename: segment, nodeid: 4, columnname: bottomrownumber1008 </strong> rownumber1006 + 1, da <code>lead</code> die nächste zeile erfordert</li> <li><strong> nodename: segment, nodeid: 4, columnname: toprownumber1007 </strong> rownumber1006 + 1, da <code>lead</code> die nächste zeile erfordert</li> <li><strong> knotenname: segment, knoten-id: 4, spaltenname: segment1014 </strong> nein <code>partition by</code> in <code>lead</code>, sodass die erste zeile 1 erhält. alle verbleibenden werden null</li> <li><strong> nodename: window spool, nodeid: 3, columnname: windowcount1015 </strong> attribut, das zeilen, die zu einem fensterrahmen gehören, unter verwendung der vorherigen zeilennummern gruppiert. der fensterrahmen für <code>lead</code> hat maximal 2 zeilen (die aktuelle und die nächste)</li> <li><strong> nodename: stream aggregate, nodeid: 2, columnname: expr1003 </strong> <code>last\_value([expr1002])</code> für <code>lead(cume\_weight)</code></li> </ul>
<p>sie können das meiste davon tun, indem sie eine login.sql verwenden. login.sql wird während der - überraschenden - anmeldung ausgeführt und aus ihrem sqlpath oder dem aktuellen verzeichnis geladen. für die beispiele, die sie gegeben haben, haben sie wirklich den schlimmsten fall gewählt.</p><p>problem ist der sqlterminator. was auch immer sie dort eingeben, der schrägstrich wird als kostenloser sql-minator beibehalten. daneben sucht sqlplus zuerst nach dem sqlterminator und führt dies vor dem scannen zum string-terminator durch. ein fehler, wenn sie mich fragen. der schrägstrich kann in einer zeichenfolge verwendet werden, solange er nicht allein in einer separaten zeile steht. sobald sqlplus das als sqlterminator angegebene zeichen findet, ignoriert es alles andere und hört auf zu lesen.</p><p>der schrägstrich kann behandelt werden, solange er nicht alleine in einer zeile steht.</p><p>login.sql enthält:</p><pre><code>prompt run login.sql show sqlterminator show sqlblanklines set sqlblanklines on set sqlterminator ';' show sqlterminator show sqlblanklines prompt ready login.sql set echo on </code></pre><p>leigh.sql enthält:</p><pre><code>insert into t1 values ('fail bc semicolon a;a / '); insert into t1 values ('fail bc solo / aa / '); insert into t1 values ('ok / not solo aa /a '); delete from t1 where a = 'c a/ '; </code></pre><p>führen sie das skript aus:</p><pre><code>sqlplus leigh/leigh@orcl @leigh sql*plus: release 10.2.0.4.0 - production on thu aug 9 22:36:20 2012 copyright (c) 1982, 2007, oracle. all rights reserved. connected to: oracle database 11g enterprise edition release 11.2.0.3.0 - 64bit production with the partitioning, real application clusters, automatic storage management, olap, data mining and real application testing options run login.sql sqlterminator ";" (hex 3b) sqlblanklines off sqlterminator ";" (hex 3b) sqlblanklines on ready login.sql sql&gt; insert into t1 values ('fail bc semicolon 2 a;a 3 / error: ora-01756: quoted string not properly terminated sql&gt; '); sp2-0042: unknown command "')" - rest of line ignored. sql&gt; sql&gt; insert into t1 values ('fail bc solo / 2 3 4 aa 5 / error: ora-01756: quoted string not properly terminated sql&gt; '); sp2-0042: unknown command "')" - rest of line ignored. sql&gt; sql&gt; insert into t1 values ('ok / not solo 2 3 4 aa 5 /a 6 '); 1 row created. sql&gt; sql&gt; delete from t1 where a = 'c 2 3 4 a/ 5 '; 0 rows deleted. </code></pre><p>sie müssen nicht mit start-/endblöcken herumspielen. der befehl sqlterminator innerhalb des befehls kann nicht verarbeitet werden, unabhängig davon, wo er sich in einer zeichenfolge befindet oder nicht. er kann keine zeilen mit schrägstrich allein in einer zeile in einer zeichenfolge verarbeiten.</p> Aloha :)


Ich hoffe, dass ich den Aufbau der Determinante richtig verstanden habe. Um die Idee zu verdeutlichen, schreibe ich die ersten 4 Fälle einfach mal auf:


Bei \(n=1\) ist nicht viel zu tun:$$D\_1=\left|\begin{array}{c}1\end{array}\right|=1$$


Bei \(n=2\) addiere ich Zeile 1 zu Zeile 2 und ziehe danach aus Zeile 2 den Faktor vor die Determinante:$$D\_2=\left|\begin{array}{c}1 & 1\\-1 & 1\end{array}\right|=\left|\begin{array}{c}1 & 1\\0 & 2\end{array}\right|=2\cdot\left|\begin{array}{c}1 & 1\\0 & 1\end{array}\right|=2$$
  



Bei \(n=3\) addiere ich Zeile 1 zu Zeile 2 und ziehe danach aus Zeile 2 den Faktor vor die Determinante:$$D\_3=\left|\begin{array}{c}1 & 1 & 0\\-1 & 1 & 4\\0 & -1 & 1\end{array}\right|=\left|\begin{array}{c}1 & 1 & 0\\0 & 2 & 4\\0 & -1 & 1\end{array}\right|=2\cdot\left|\begin{array}{c}1 & 1 & 0\\0 & 1 & 2\\0 & -1 & 1\end{array}\right|$$Jetzt addiere ich weiter Zeile 2 zu Zeile 3 und ziehe aus Zeile 3 den Faktor vor:$$\phantom{D\_3}=2\cdot\left|\begin{array}{c}1 & 1 & 0\\0 & 1 & 2\\0 & 0 & 3\end{array}\right|=2\cdot3\cdot\left|\begin{array}{c}1 & 1 & 0\\0 & 1 & 2\\0 & 0 & 1\end{array}\right|=6$$


Bei \(n=4\) wiederhole ich zunächst alle Schritte vom Fall \(n=3\):


$$D\_4=\left|\begin{array}{c}1 & 1 & 0 & 0\\-1 & 1 & 4 & 0\\0 & -1 & 1 & 9\\0 & 0 & -1 & 1\end{array}\right|=\left|\begin{array}{c}1 & 1 & 0 & 0\\0 & 2 & 4 & 0\\0 & -1 & 1 & 9\\0 & 0 & -1 & 1\end{array}\right|=2\cdot\left|\begin{array}{c}1 & 1 & 0 & 0\\0 & 1 & 2 & 0\\0 & -1 & 1 & 9\\0 & 0 & -1 & 1\end{array}\right|$$$$\phantom{D\_4}=2\cdot\left|\begin{array}{c}1 & 1 & 0 & 0\\0 & 1 & 2 & 0\\0 & 0 & 3 & 9\\0 & 0 & -1 & 1\end{array}\right|=2\cdot3\cdot\left|\begin{array}{c}\underline1 & \underline1 & \underline0 & 0\\\underline0 & \underline1 & \underline2 & 0\\\underline0 & \underline0 & \underline1 & 3\\0 & 0 & -1 & 1\end{array}\right|$$Man erkennt die letzte Determinante aus der Berechnung von \(D\_3\) wieder (unterstrichene Einträge), nur sind jetzt noch die 3 Elemente in der rechten unteren Ecke dazu gekommen, alle anderen zusätzlichen Einträge sind 0. Wir können daher noch einen weiteren Schritt (Addition der vorletzten Zeile zur letzten und Faktor herausziehen) durchführen:$$\phantom{D\_4}=2\cdot3\cdot\left|\begin{array}{c}1 & 1 & 0 & 0\\0 & 1 & 2 & 0\\0 & 0 & 1 & 3\\0 & 0 & 0 & 4\end{array}\right|=2\cdot3\cdot4\cdot\left|\begin{array}{c}1 & 1 & 0 & 0\\0 & 1 & 2 & 0\\0 & 0 & 1 & 3\\0 & 0 & 0 & 1\end{array}\right|=24$$


Damit ist klar, dass: \(D\_n=n!\)


Dieses Vorgehen kannst du, wenn verlangt, noch in einen sauberen Induktionsbeweis packen. Die Beweisidee sollte zumindest jetzt klar sein ;) <p>hinweis: ab azure sql database v12 gelten diese einschränkungen nicht mehr.</p><p>das ist kein "primärindex". es gibt so etwas wie einen "primärschlüssel" und es gibt auch so etwas wie einen "clustered index". unterschiedliche konzepte, oft verwirrt. lassen sie uns angesichts dieser unterscheidung die frage noch einmal betrachten:</p><p>q1) kann der clustered-index in einer sql azure-tabelle geändert werden?<br> a: ja. verwenden sie <code>with (drop\_existing=on)</code>:</p><pre><code>create table friend ( userid int not null, id int not null); go create clustered index cdxfriend on friend (userid, id); go create clustered index cdxfriend on friend (id, userid) with (drop\_existing=on); go </code></pre><p>f2) kann der clustered-index einer tabelle mit einer primärschlüsseleinschränkung geändert werden?<br> a: ja, wie oben, solange die primärschlüsseleinschränkung nicht über den clustered-index erzwungen wird:</p><pre><code>create table friend ( userid int not null, id int not null identity(1,1), constraint pk\_friend primary key nonclustered (id)); create clustered index cdxfriend on friend (userid, id); go create clustered index cdxfriend on friend (id, userid) with (drop\_existing=on); go </code></pre><p>f3) kann die primärschlüsseleinschränkung einer tabelle geändert werden?<br> a: ja, solange die primäre einschränkung nicht über den clustered-index erzwungen wird:</p><pre><code>create table friend ( userid int not null, id int not null identity(1,1), constraint pk\_friend primary key nonclustered (id)); go create clustered index cdxfriend on friend (userid, id); go alter table friend drop constraint pk\_friend; alter table friend add constraint pk\_friend primary key nonclustered (userid) go </code></pre><p>f4) kann der primärschlüssel einer tabelle geändert werden, wenn er über den clustered-index erzwungen wird?<br> a: ja, wenn die tabelle nie zeilen hatte:</p><pre><code>create table friend ( userid int not null, id int not null identity(1,1), constraint pk\_friend primary key clustered (userid, id)); go alter table friend drop constraint pk\_friend; alter table friend add constraint pk\_friend primary key clustered (id, userid) go </code></pre><p>f5) kann der primärschlüssel einer tabelle geändert werden, wenn er über den clustered-index erzwungen wird, wenn die tabelle gefüllt ist?<br> a: nein. jede operation, die einen aufgefüllten clustered-index in einen heap konvertiert, wird in sql azure blockiert <em>, auch wenn die tabelle leer ist </em>:</p><pre><code>create table friend ( userid int not null, id int not null identity(1,1), constraint pk\_friend primary key clustered (userid, id)); go insert into friend (userid) values (1); delete from friend; go alter table friend drop constraint pk\_friend; </code></pre><p>als randnotiz: die einschränkung kann geändert werden, wenn die tabelle <em> abgeschnitten ist </em>.</p><p>die problemumgehung zum ändern der pk-einschränkung einer aufgefüllten tabelle besteht darin, das gute alte <a href="http://msdn.microsoft.com/en-us/library/ms188351.aspx"><code>sp\_rename</code></a> trick:</p><pre><code>create table friend ( userid int not null, id int not null identity(1,1), constraint pk\_friend primary key clustered (userid, id)); go insert into friend (userid) values (1); go create table friendnew ( userid int not null, id int not null identity(1,1), constraint pk\_friend\_new primary key clustered (id, userid)); go set identity\_insert friendnew on; insert into friendnew (userid, id) select userid, id from friend; set identity\_insert friendnew off; go begin transaction exec sp\_rename 'friend', 'friendold'; exec sp\_rename 'friendnew', 'friend'; commit; go sp\_help 'friend'; </code></pre><p>das <code>sp\_rename</code> ansatz hat einige probleme, vor allem, dass berechtigungen für die tabelle während des umbenennens nicht übertragen werden, sowie fremdschlüsseleinschränkungen.</p> <p>es gibt optionen, die ihnen dabei helfen:</p><pre><code> --insert-ignore insert rows with insert ignore. --replace use replace into instead of insert into. -t, --no-create-info don't write table creation info. </code></pre><p>denken sie an dieses paradigma</p><ul> <li>mysqldump alles von db1 in dump1</li> <li>laden sie dump1 in db3</li> <li>mysqldump alles aus db2 mit --replace (oder --insert-ignore) und --no-create-info in dump2</li> <li>laden sie dump2 in db3</li> </ul> <p>wenn dies meine entwurfsentscheidung wäre, würde ich wahrscheinlich eher eine option c (modifizierte option a) wählen.</p><h3>erstens, warum nicht 'option b':</h3><p>zum einen gefällt mir die klarheit, dass jedes produkt seinen eigenen tisch hat. wenn es sich nur um eine große tabelle mit einem feld zur bestimmung des typs handelt, ist die beziehung nicht so klar.</p><p>zum anderen würde die indizierungsstrategie immer erfordern, dass dieses typfeld aufgelistet wird. da es sich nur um 4 typen handelt, ist die indexkardinalität extrem niedrig (<code>select * from product\_table where type='x'</code> führt im grunde sowieso einen vollständigen tabellenscan durch)</p><h3>option c.</h3><ul> <li>erstellen sie eine übergeordnete tabelle, die nur die spalten enthält, die alle typen gemeinsam nutzen</li> <li>erstellen sie jeden produkttyp als eigene tabelle mit ihren einzelnen spalten, mit einem zusätzlichen: ein link zur übergeordneten tabelle</li> <li>erstellen sie jede 'link'-tabelle: product\_option, model\_option usw. mit links zu den jeweiligen schlüsseln.</li> <li>für diejenigen mit wechselseitigen links (model\_option, option\_model) erstellen sie auch diese tabellen. dies erhöht die klarheit ihrer joins für alle, die sich das ansehen.</li> </ul><p>der nachteil ist die komplexität, sicherzustellen, dass waisen vermieden werden, wenn dinge aktualisiert/gelöscht werden, und zunächst die abfragen zu entwerfen, die diese tabellen verwenden.</p> weiß nicht, obs klappt, Diagramme sind auch meine Stärke nicht, im Kopf schon gleich gar nicht, aber versuch doch mal, die x-Achse manuell fallend festzulegen, also nicht von zB min 0..max100, sondern von 100..0


Oder Du manipulierst die Werte: in A11: =max($A10:$E10)-A10, rüberziehen.  

(oder =100-A10). Wäre **alternativ** auch für y denkbar (nicht beides gleichzeitig!).
Auch -A10 könnte gehen, wenn Dich das negative Vorzeichen nicht stört. 


Dabei kannst Du vor E:E immer wieder eine Spalte einsetzen. das Diagramm übernimmt das aber nur vielleicht, wenn Du schreibst **"='Tabelle1'!$A$10:$E$10"** . Alle anderen Bezüge entsprechend. <h2>aufgrund der empfehlungen in den kommentaren habe ich begonnen, <a href="http://tools.tortoisesvn.net/grepwin.html">grepwin</a> zu verwenden, und es ist fantastisch und <em> kostenlos</em>.</h2><hr><p>(ich bin immer noch ein fan von <a href="http://www.powergrep.com/">powergrep</a> , aber ich benutze es nicht mehr.)</p><p>ich weiß, dass sie es bereits erwähnt haben, aber powergrep ist <strong> fantastisch </strong>.</p><p>einige meiner lieblingsfunktionen sind:</p><ul> <li>klicken sie mit der rechten maustaste auf einen ordner, um powergrep darauf auszuführen</li> <li>verwenden sie reguläre ausdrücke oder wörtlichen text</li> <li>geben sie platzhalter für dateien an, die ein- und ausgeschlossen werden sollen</li> <li>suchen &amp; ersetzen</li> <li>der vorschaumodus ist gut, da sie sicherstellen können, dass sie das ersetzen, was sie beabsichtigen.</li> </ul><p>jetzt ist mir klar, dass die anderen grep-tools all das tun können. es ist nur so, dass powergrep alle funktionen in einer sehr benutzerfreundlichen benutzeroberfläche zusammenfasst.</p><p><strong> <em> von den gleichen wundervollen leuten, die sie mitgebracht haben <a href="http://en.wikipedia.org/wiki/regexbuddy">regexbuddy</a> und denen ich nichts mehr zu tun habe, als ihre sachen zu lieben.</em> </strong> (es sollte beachtet werden, dass regexbuddy eine basisversion von grep (für windows) selbst enthält und viel weniger kostet als powergrep.)</p><hr><h2>zusätzliche lösungen</h2><h3>vorhandene windows-befehle</h3><ul> <li> <a href="http://ss64.com/nt/findstr.html"> findstr </a> </li> <li> <a href="https://technet.microsoft.com/en-us/library/hh849903.aspx">auswahlzeichenfolge</a> in powershell</li> </ul><h3>linux-befehlsimplementierungen unter windows</h3><ul> <li> <a href="http://en.wikipedia.org/wiki/cygwin">cygwin</a> </li> <li> <a href="https://github.com/dthree/cash">bargeld</a> </li> </ul><h3>grep-tools mit grafischer oberfläche</h3><ul> <li> <a href="http://astrogrep.sourceforge.net/screenshots/">astrogrep</a> </li> <li> <a href="https://www.baremetalsoft.com/baregrep/">baregrep</a> </li> <li> <a href="http://stefanstools.sourceforge.net/grepwin.html">grepwin</a> </li> </ul><h3>zusätzliche grep-tools</h3><ul> <li> <a href="http://code.google.com/p/dngrep/">dngrep</a> </li> </ul> Ich sehe nicht, wie Sie schneller als mit SqlBulkCopy ausführen können. Auf unserem Projekt können wir 250K Zeilen in ungefähr 3 Minuten importieren, also scheint Ihre Rate ungefähr richtig zu sein. 

 Ich denke nicht, dass es parallel dazu helfen würde, selbst wenn es technisch möglich wäre. Wir führen nur jeweils einen Import aus, ansonsten beginnt SQL Azure damit, unsere Anforderungen zu terminieren. 

 In der Tat ist es manchmal nicht möglich, eine große Group-by-Abfrage gleichzeitig mit dem Import auszuführen. SQL Azure führt eine Menge Arbeit aus, um die Servicequalität zu gewährleisten. Dazu gehört auch das Zeitlimit für Anforderungen, die zu lange dauern, zu viele Ressourcen usw 

 Wenn Sie also mehrere große Masseneinsätze zur selben Zeit machen, werden Sie *wahrscheinlich* eine Auszeit haben. Wenn eine Spalte in der Datenbank nullfähig ist, hat die Ergebnisklasse auch eine nullfähige Eigenschaft. 

 Wenn beispielsweise eine `Order` eine `Datetime` Spalte mit dem Namen `CreatedDate` , sieht die resultierende `Order` Klasse in etwa wie folgt aus: 


```
public partial class Order: EntityObject
{
  ...
  private DateTime? _createdDate;
  ...
}

```
  Wenn \_ `createdDate` keinen Wert hat, wenn Sie `ObjectContext.SaveChanges()` aufrufen, wird eine `System.DBNull` automatisch als Teil des Befehls insert oder update an die Datenbank gesendet. 

 Hoffe das hilft Alex Der Grund für das Problem war ein Fehler in der Effort-Datenbank. Wenn die berechnete Spalte auf nicht nullwertfähigen Spalten basiert, kann die berechnete Spalte auch automatisch nicht nullwertfähig werden. Daher erwartete die Effort-Datenbank einen Wert ungleich Null. Mit dem neuesten Update ist das Problem behoben. Sie müssen das globale `EntityFrameworkEffortManager.UseDefaultForNotNullable` Flag auf true setzen. 

 Siehe die [Ausgabe auf Github](https://github.com/zzzprojects/EntityFramework-Effort/issues/62#issuecomment-471199839)